/* toke.c
*
- * Copyright (c) 1991-1994, Larry Wall
+ * Copyright (c) 1991-1997, Larry Wall
*
* You may distribute under the terms of either the GNU General Public
* License or the Artistic License, as specified in the README file.
static void check_uni _((void));
static void force_next _((I32 type));
+static char *force_version _((char *start));
static char *force_word _((char *start, int token, int check_keyword, int allow_pack, int allow_tick));
static SV *q _((SV *sv));
static char *scan_const _((char *start));
static char *scan_formline _((char *s));
static char *scan_heredoc _((char *s));
-static char *scan_ident _((char *s, char *send, char *dest, I32 ck_uni));
+static char *scan_ident _((char *s, char *send, char *dest, STRLEN destlen,
+ I32 ck_uni));
static char *scan_inputsymbol _((char *start));
static char *scan_pat _((char *start));
static char *scan_str _((char *start));
static char *scan_subst _((char *start));
static char *scan_trans _((char *start));
-static char *scan_word _((char *s, char *dest, int allow_package, STRLEN *slp));
+static char *scan_word _((char *s, char *dest, STRLEN destlen,
+ int allow_package, STRLEN *slp));
static char *skipspace _((char *s));
static void checkcomma _((char *s, char *name, char *what));
static void force_ident _((char *s, int kind));
static void no_op _((char *what, char *s));
static void set_csh _((void));
static I32 sublex_done _((void));
+static I32 sublex_push _((void));
static I32 sublex_start _((void));
#ifdef CRIPPLED_CC
static int uni _((I32 f, char *s));
#endif
-static char * filter_gets _((SV *sv, FILE *fp));
+static char * filter_gets _((SV *sv, PerlIO *fp, STRLEN append));
static void restore_rsfp _((void *f));
+static void restore_expect _((void *e));
+static void restore_lex_expect _((void *e));
+
+static char ident_too_long[] = "Identifier too long";
+
+static char *linestart; /* beg. of most recently read line */
+
+static char pending_ident; /* pending identifier lookup */
+
+static struct {
+ I32 super_state; /* lexer state to save */
+ I32 sub_inwhat; /* "lex_inwhat" to use */
+ OP *sub_op; /* "lex_op" to use */
+} sublex_info;
/* The following are arranged oddly so that the guard on the switch statement
* can get by with a single comparison (if the compiler is smart enough).
*/
-#define LEX_NORMAL 9
-#define LEX_INTERPNORMAL 8
-#define LEX_INTERPCASEMOD 7
-#define LEX_INTERPSTART 6
-#define LEX_INTERPEND 5
-#define LEX_INTERPENDMAYBE 4
-#define LEX_INTERPCONCAT 3
-#define LEX_INTERPCONST 2
-#define LEX_FORMLINE 1
-#define LEX_KNOWNEXT 0
+/* #define LEX_NOTPARSING 11 is done in perl.h. */
+
+#define LEX_NORMAL 10
+#define LEX_INTERPNORMAL 9
+#define LEX_INTERPCASEMOD 8
+#define LEX_INTERPPUSH 7
+#define LEX_INTERPSTART 6
+#define LEX_INTERPEND 5
+#define LEX_INTERPENDMAYBE 4
+#define LEX_INTERPCONCAT 3
+#define LEX_INTERPCONST 2
+#define LEX_FORMLINE 1
+#define LEX_KNOWNEXT 0
#ifdef I_FCNTL
#include <fcntl.h>
#include <sys/file.h>
#endif
+/* XXX If this causes problems, set i_unistd=undef in the hint file. */
+#ifdef I_UNISTD
+# include <unistd.h> /* Needed for execv() */
+#endif
+
+
#ifdef ff_next
#undef ff_next
#endif
#define OLDLOP(f) return(yylval.ival=f,expect = XTERM,bufptr = s,(int)LSTOP)
static int
-ao(toketype)
-int toketype;
+ao(int toketype)
{
if (*bufptr == '=') {
bufptr++;
}
static void
-no_op(what, s)
-char *what;
-char *s;
+no_op(char *what, char *s)
{
- char tmpbuf[128];
char *oldbp = bufptr;
- bool is_first = (oldbufptr == SvPVX(linestr));
+ bool is_first = (oldbufptr == linestart);
+
bufptr = s;
- sprintf(tmpbuf, "%s found where operator expected", what);
- yywarn(tmpbuf);
+ yywarn(form("%s found where operator expected", what));
if (is_first)
warn("\t(Missing semicolon on previous line?)\n");
else if (oldoldbufptr && isIDFIRST(*oldoldbufptr)) {
}
static void
-missingterm(s)
-char *s;
+missingterm(char *s)
{
char tmpbuf[3];
char q;
if (s) {
char *nl = strrchr(s,'\n');
- if (nl)
+ if (nl)
*nl = '\0';
}
else if (multi_close < 32 || multi_close == 127) {
*tmpbuf = '^';
- tmpbuf[1] = multi_close ^ 64;
+ tmpbuf[1] = toCTRL(multi_close);
s = "\\n";
tmpbuf[2] = '\0';
s = tmpbuf;
}
void
-deprecate(s)
-char *s;
+deprecate(char *s)
{
if (dowarn)
warn("Use of %s is deprecated", s);
}
static void
-depcom()
+depcom(void)
{
deprecate("comma-less variable list");
}
+#ifdef WIN32
+
+static I32
+win32_textfilter(int idx, SV *sv, int maxlen)
+{
+ I32 count = FILTER_READ(idx+1, sv, maxlen);
+ if (count > 0 && !maxlen)
+ win32_strip_return(sv);
+ return count;
+}
+#endif
+
+
void
-lex_start(line)
-SV *line;
+lex_start(SV *line)
{
+ dTHR;
char *s;
STRLEN len;
- SAVEINT(lex_dojoin);
- SAVEINT(lex_brackets);
- SAVEINT(lex_fakebrack);
- SAVEINT(lex_casemods);
- SAVEINT(lex_starts);
- SAVEINT(lex_state);
+ SAVEI32(lex_dojoin);
+ SAVEI32(lex_brackets);
+ SAVEI32(lex_fakebrack);
+ SAVEI32(lex_casemods);
+ SAVEI32(lex_starts);
+ SAVEI32(lex_state);
SAVESPTR(lex_inpat);
- SAVEINT(lex_inwhat);
- SAVEINT(curcop->cop_line);
+ SAVEI32(lex_inwhat);
+ SAVEI16(curcop->cop_line);
SAVEPPTR(bufptr);
SAVEPPTR(bufend);
SAVEPPTR(oldbufptr);
SAVEPPTR(oldoldbufptr);
+ SAVEPPTR(linestart);
SAVESPTR(linestr);
SAVEPPTR(lex_brackstack);
SAVEPPTR(lex_casestack);
SAVEDESTRUCTOR(restore_rsfp, rsfp);
+ SAVESPTR(lex_stuff);
+ SAVEI32(lex_defer);
+ SAVESPTR(lex_repl);
+ SAVEDESTRUCTOR(restore_expect, tokenbuf + expect); /* encode as pointer */
+ SAVEDESTRUCTOR(restore_lex_expect, tokenbuf + expect);
lex_state = LEX_NORMAL;
lex_defer = 0;
*lex_casestack = '\0';
lex_dojoin = 0;
lex_starts = 0;
- if (lex_stuff)
- SvREFCNT_dec(lex_stuff);
lex_stuff = Nullsv;
- if (lex_repl)
- SvREFCNT_dec(lex_repl);
lex_repl = Nullsv;
lex_inpat = 0;
lex_inwhat = 0;
sv_catpvn(linestr, "\n;", 2);
}
SvTEMP_off(linestr);
- oldoldbufptr = oldbufptr = bufptr = SvPVX(linestr);
+ oldoldbufptr = oldbufptr = bufptr = linestart = SvPVX(linestr);
bufend = bufptr + SvCUR(linestr);
SvREFCNT_dec(rs);
rs = newSVpv("\n", 1);
}
void
-lex_end()
+lex_end(void)
{
+ doextract = FALSE;
}
static void
-restore_rsfp(f)
-void *f;
+restore_rsfp(void *f)
{
- FILE *fp = (FILE*)f;
+ PerlIO *fp = (PerlIO*)f;
- if (rsfp == stdin)
- clearerr(rsfp);
+ if (rsfp == PerlIO_stdin())
+ PerlIO_clearerr(rsfp);
else if (rsfp && (rsfp != fp))
- fclose(rsfp);
+ PerlIO_close(rsfp);
rsfp = fp;
}
static void
-incline(s)
-char *s;
+restore_expect(void *e)
+{
+ /* a safe way to store a small integer in a pointer */
+ expect = (expectation)((char *)e - tokenbuf);
+}
+
+static void
+restore_lex_expect(void *e)
{
+ /* a safe way to store a small integer in a pointer */
+ lex_expect = (expectation)((char *)e - tokenbuf);
+}
+
+static void
+incline(char *s)
+{
+ dTHR;
char *t;
char *n;
char ch;
}
static char *
-skipspace(s)
-register char *s;
+skipspace(register char *s)
{
+ dTHR;
if (lex_formbrack && lex_brackets <= lex_formbrack) {
while (s < bufend && (*s == ' ' || *s == '\t'))
s++;
return s;
}
for (;;) {
+ STRLEN prevlen;
while (s < bufend && isSPACE(*s))
s++;
if (s < bufend && *s == '#') {
}
if (s < bufend || !rsfp || lex_state != LEX_NORMAL)
return s;
- if ((s = filter_gets(linestr, rsfp)) == Nullch) {
+ if ((s = filter_gets(linestr, rsfp, (prevlen = SvCUR(linestr)))) == Nullch) {
if (minus_n || minus_p) {
- sv_setpv(linestr,minus_p ? ";}continue{print" : "");
+ sv_setpv(linestr,minus_p ?
+ ";}continue{print or die qq(-p destination: $!\\n)" :
+ "");
sv_catpv(linestr,";}");
minus_n = minus_p = 0;
}
else
sv_setpv(linestr,";");
- oldoldbufptr = oldbufptr = bufptr = s = SvPVX(linestr);
+ oldoldbufptr = oldbufptr = bufptr = s = linestart = SvPVX(linestr);
bufend = SvPVX(linestr) + SvCUR(linestr);
if (preprocess && !in_eval)
- (void)my_pclose(rsfp);
- else if ((FILE*)rsfp == stdin)
- clearerr(stdin);
+ (void)PerlProc_pclose(rsfp);
+ else if ((PerlIO*)rsfp == PerlIO_stdin())
+ PerlIO_clearerr(rsfp);
else
- (void)fclose(rsfp);
+ (void)PerlIO_close(rsfp);
+ if (e_fp == rsfp)
+ e_fp = Nullfp;
rsfp = Nullfp;
return s;
}
- oldoldbufptr = oldbufptr = bufptr = s;
- bufend = bufptr + SvCUR(linestr);
+ linestart = bufptr = s + prevlen;
+ bufend = s + SvCUR(linestr);
+ s = bufptr;
incline(s);
- if (perldb && curstash != debstash) {
+ if (PERLDB_LINE && curstash != debstash) {
SV *sv = NEWSV(85,0);
sv_upgrade(sv, SVt_PVMG);
- sv_setsv(sv,linestr);
+ sv_setpvn(sv,bufptr,bufend-bufptr);
av_store(GvAV(curcop->cop_filegv),(I32)curcop->cop_line,sv);
}
}
}
static void
-check_uni() {
+check_uni(void) {
char *s;
char ch;
char *t;
#define UNI(f) return uni(f,s)
static int
-uni(f,s)
-I32 f;
-char *s;
+uni(I32 f, char *s)
{
yylval.ival = f;
expect = XTERM;
#define LOP(f,x) return lop(f,x,s)
static I32
-lop(f,x,s)
-I32 f;
-expectation x;
-char *s;
+lop(I32 f, expectation x, char *s)
{
+ dTHR;
yylval.ival = f;
CLINE;
expect = x;
}
static void
-force_next(type)
-I32 type;
+force_next(I32 type)
{
nexttype[nexttoke] = type;
nexttoke++;
}
static char *
-force_word(start,token,check_keyword,allow_pack,allow_tick)
-register char *start;
-int token;
-int check_keyword;
-int allow_pack;
-int allow_tick;
+force_word(register char *start, int token, int check_keyword, int allow_pack, int allow_initial_tick)
{
register char *s;
STRLEN len;
s = start;
if (isIDFIRST(*s) ||
(allow_pack && *s == ':') ||
- (allow_tick && *s == '\'') )
+ (allow_initial_tick && *s == '\'') )
{
- s = scan_word(s, tokenbuf, allow_pack, &len);
+ s = scan_word(s, tokenbuf, sizeof tokenbuf, allow_pack, &len);
if (check_keyword && keyword(tokenbuf, len))
return start;
if (token == METHOD) {
}
static void
-force_ident(s, kind)
-register char *s;
-int kind;
+force_ident(register char *s, int kind)
{
if (s && *s) {
- OP* op = (OP*)newSVOP(OP_CONST, 0, newSVpv(s,0));
- nextval[nexttoke].opval = op;
+ OP* o = (OP*)newSVOP(OP_CONST, 0, newSVpv(s,0));
+ nextval[nexttoke].opval = o;
force_next(WORD);
if (kind) {
- op->op_private = OPpCONST_ENTERED;
- gv_fetchpv(s, TRUE,
+ dTHR; /* just for in_eval */
+ o->op_private = OPpCONST_ENTERED;
+ /* XXX see note in pp_entereval() for why we forgo typo
+ warnings if the symbol must be introduced in an eval.
+ GSAR 96-10-12 */
+ gv_fetchpv(s, in_eval ? (GV_ADDMULTI | 8) : TRUE,
kind == '$' ? SVt_PV :
kind == '@' ? SVt_PVAV :
kind == '%' ? SVt_PVHV :
}
}
+static char *
+force_version(char *s)
+{
+ OP *version = Nullop;
+
+ s = skipspace(s);
+
+ /* default VERSION number -- GBARR */
+
+ if(isDIGIT(*s)) {
+ char *d;
+ int c;
+ for( d=s, c = 1; isDIGIT(*d) || *d == '_' || (*d == '.' && c--); d++);
+ if((*d == ';' || isSPACE(*d)) && *(skipspace(d)) != ',') {
+ s = scan_num(s);
+ /* real VERSION number -- GBARR */
+ version = yylval.opval;
+ }
+ }
+
+ /* NOTE: The parser sees the package name and the VERSION swapped */
+ nextval[nexttoke].opval = version;
+ force_next(WORD);
+
+ return (s);
+}
+
static SV *
-q(sv)
-SV *sv;
+q(SV *sv)
{
register char *s;
register char *send;
}
static I32
-sublex_start()
+sublex_start(void)
{
register I32 op_type = yylval.ival;
return THING;
}
if (op_type == OP_CONST || op_type == OP_READLINE) {
- yylval.opval = (OP*)newSVOP(op_type, 0, q(lex_stuff));
+ SV *sv = q(lex_stuff);
+ STRLEN len;
+ char *p = SvPV(sv, len);
+ yylval.opval = (OP*)newSVOP(op_type, 0, newSVpv(p, len));
+ SvREFCNT_dec(sv);
lex_stuff = Nullsv;
return THING;
}
- push_scope();
- SAVEINT(lex_dojoin);
- SAVEINT(lex_brackets);
- SAVEINT(lex_fakebrack);
- SAVEINT(lex_casemods);
- SAVEINT(lex_starts);
- SAVEINT(lex_state);
+ sublex_info.super_state = lex_state;
+ sublex_info.sub_inwhat = op_type;
+ sublex_info.sub_op = lex_op;
+ lex_state = LEX_INTERPPUSH;
+
+ expect = XTERM;
+ if (lex_op) {
+ yylval.opval = lex_op;
+ lex_op = Nullop;
+ return PMFUNC;
+ }
+ else
+ return FUNC;
+}
+
+static I32
+sublex_push(void)
+{
+ dTHR;
+ ENTER;
+
+ lex_state = sublex_info.super_state;
+ SAVEI32(lex_dojoin);
+ SAVEI32(lex_brackets);
+ SAVEI32(lex_fakebrack);
+ SAVEI32(lex_casemods);
+ SAVEI32(lex_starts);
+ SAVEI32(lex_state);
SAVESPTR(lex_inpat);
- SAVEINT(lex_inwhat);
- SAVEINT(curcop->cop_line);
+ SAVEI32(lex_inwhat);
+ SAVEI16(curcop->cop_line);
SAVEPPTR(bufptr);
SAVEPPTR(oldbufptr);
SAVEPPTR(oldoldbufptr);
+ SAVEPPTR(linestart);
SAVESPTR(linestr);
SAVEPPTR(lex_brackstack);
SAVEPPTR(lex_casestack);
linestr = lex_stuff;
lex_stuff = Nullsv;
- bufend = bufptr = oldbufptr = oldoldbufptr = SvPVX(linestr);
+ bufend = bufptr = oldbufptr = oldoldbufptr = linestart = SvPVX(linestr);
bufend += SvCUR(linestr);
SAVEFREESV(linestr);
lex_state = LEX_INTERPCONCAT;
curcop->cop_line = multi_start;
- lex_inwhat = op_type;
- if (op_type == OP_MATCH || op_type == OP_SUBST)
- lex_inpat = lex_op;
+ lex_inwhat = sublex_info.sub_inwhat;
+ if (lex_inwhat == OP_MATCH || lex_inwhat == OP_SUBST)
+ lex_inpat = sublex_info.sub_op;
else
- lex_inpat = 0;
+ lex_inpat = Nullop;
- expect = XTERM;
- force_next('(');
- if (lex_op) {
- yylval.opval = lex_op;
- lex_op = Nullop;
- return PMFUNC;
- }
- else
- return FUNC;
+ return '(';
}
static I32
-sublex_done()
+sublex_done(void)
{
if (!lex_starts++) {
expect = XOPERATOR;
if (lex_repl && (lex_inwhat == OP_SUBST || lex_inwhat == OP_TRANS)) {
linestr = lex_repl;
lex_inpat = 0;
- bufend = bufptr = oldbufptr = oldoldbufptr = SvPVX(linestr);
+ bufend = bufptr = oldbufptr = oldoldbufptr = linestart = SvPVX(linestr);
bufend += SvCUR(linestr);
SAVEFREESV(linestr);
lex_dojoin = FALSE;
return ',';
}
else {
- pop_scope();
+ LEAVE;
bufend = SvPVX(linestr);
bufend += SvCUR(linestr);
expect = XOPERATOR;
}
}
+/*
+ scan_const
+
+ Extracts a pattern, double-quoted string, or transliteration. This
+ is terrifying code.
+
+ It looks at lex_inwhat and lex_inpat to find out whether it's
+ processing a pattern (lex_inpat is true), a transliteration
+ (lex_inwhat & OP_TRANS is true), or a double-quoted string.
+
+ In patterns:
+ backslashes:
+ double-quoted style: \r and \n
+ regexp special ones: \D \s
+ constants: \x3
+ backrefs: \1 (deprecated in substitution replacements)
+ case and quoting: \U \Q \E
+ stops on @ and $, but not for $ as tail anchor
+
+ In transliterations:
+ characters are VERY literal, except for - not at the start or end
+ of the string, which indicates a range. scan_const expands the
+ range to the full set of intermediate characters.
+
+ In double-quoted strings:
+ backslashes:
+ double-quoted style: \r and \n
+ constants: \x3
+ backrefs: \1 (deprecated)
+ case and quoting: \U \Q \E
+ stops on @ and $
+
+ scan_const does *not* construct ops to handle interpolated strings.
+ It stops processing as soon as it finds an embedded $ or @ variable
+ and leaves it to the caller to work out what's going on.
+
+ @ in pattern could be: @foo, @{foo}, @$foo, @'foo, @:foo.
+
+ $ in pattern could be $foo or could be tail anchor. Assumption:
+ it's a tail anchor if $ is the last thing in the string, or if it's
+ followed by one of ")| \n\t"
+
+ \1 (backreferences) are turned into $1
+
+ The structure of the code is
+ while (there's a character to process) {
+ handle transliteration ranges
+ skip regexp comments
+ skip # initiated comments in //x patterns
+ check for embedded @foo
+ check for embedded scalars
+ if (backslash) {
+ leave intact backslashes from leave (below)
+ deprecate \1 in strings and sub replacements
+ handle string-changing backslashes \l \U \Q \E, etc.
+ switch (what was escaped) {
+ handle - in a transliteration (becomes a literal -)
+ handle \132 octal characters
+ handle 0x15 hex characters
+ handle \cV (control V)
+ handle printf backslashes (\f, \r, \n, etc)
+ } (end switch)
+ } (end if backslash)
+ } (end while character to read)
+
+*/
+
static char *
-scan_const(start)
-char *start;
+scan_const(char *start)
{
- register char *send = bufend;
- SV *sv = NEWSV(93, send - start);
- register char *s = start;
- register char *d = SvPVX(sv);
- bool dorange = FALSE;
- I32 len;
- char *leave =
+ register char *send = bufend; /* end of the constant */
+ SV *sv = NEWSV(93, send - start); /* sv for the constant */
+ register char *s = start; /* start of the constant */
+ register char *d = SvPVX(sv); /* destination for copies */
+ bool dorange = FALSE; /* are we in a translit range? */
+ I32 len; /* ? */
+
+ /*
+ leave is the set of acceptably-backslashed characters.
+
+ I do *not* understand why there's the double hook here.
+ */
+ char *leaveit =
lex_inpat
? "\\.^$@AGZdDwWsSbB+*?|()-nrtfeaxc0123456789[{]} \t\n\r\f\v#"
: (lex_inwhat & OP_TRANS)
: "";
while (s < send || dorange) {
+ /* get transliterations out of the way (they're most literal) */
if (lex_inwhat == OP_TRANS) {
+ /* expand a range A-Z to the full set of characters. AIE! */
if (dorange) {
- I32 i;
- I32 max;
- i = d - SvPVX(sv);
- SvGROW(sv, SvLEN(sv) + 256);
- d = SvPVX(sv) + i;
- d -= 2;
- max = (U8)d[1];
+ I32 i; /* current expanded character */
+ I32 max; /* last character in range */
+
+ i = d - SvPVX(sv); /* remember current offset */
+ SvGROW(sv, SvLEN(sv) + 256); /* expand the sv -- there'll never be more'n 256 chars in a range for it to grow by */
+ d = SvPVX(sv) + i; /* restore d after the grow potentially has changed the ptr */
+ d -= 2; /* eat the first char and the - */
+
+ max = (U8)d[1]; /* last char in range */
+
for (i = (U8)*d; i <= max; i++)
*d++ = i;
+
+ /* mark the range as done, and continue */
dorange = FALSE;
continue;
}
+
+ /* range begins (ignore - as first or last char) */
else if (*s == '-' && s+1 < send && s != start) {
dorange = TRUE;
s++;
}
}
- else if (*s == '(' && lex_inpat && s[1] == '?' && s[2] == '#') {
- while (s < send && *s != ')')
- *d++ = *s++;
+
+ /* if we get here, we're not doing a transliteration */
+
+ /* skip for regexp comments /(?#comment)/ */
+ else if (*s == '(' && lex_inpat && s[1] == '?') {
+ if (s[2] == '#') {
+ while (s < send && *s != ')')
+ *d++ = *s++;
+ } else if (s[2] == '{') { /* This should march regcomp.c */
+ I32 count = 1;
+ char *regparse = s + 3;
+ char c;
+
+ while (count && (c = *regparse)) {
+ if (c == '\\' && regparse[1])
+ regparse++;
+ else if (c == '{')
+ count++;
+ else if (c == '}')
+ count--;
+ regparse++;
+ }
+ if (*regparse == ')')
+ regparse++;
+ else
+ yyerror("Sequence (?{...}) not terminated or not {}-balanced");
+ while (s < regparse && *s != ')')
+ *d++ = *s++;
+ }
}
+
+ /* likewise skip #-initiated comments in //x patterns */
else if (*s == '#' && lex_inpat &&
((PMOP*)lex_inpat)->op_pmflags & PMf_EXTENDED) {
while (s+1 < send && *s != '\n')
*d++ = *s++;
}
+
+ /* check for embedded arrays (@foo, @:foo, @'foo, @{foo}, @$foo) */
else if (*s == '@' && s[1] && (isALNUM(s[1]) || strchr(":'{$", s[1])))
break;
+
+ /* check for embedded scalars. only stop if we're sure it's a
+ variable.
+ */
else if (*s == '$') {
if (!lex_inpat) /* not a regexp, so $ must be var */
break;
- if (s + 1 < send && !strchr(")| \n\t", s[1]))
+ if (s + 1 < send && !strchr("()| \n\t", s[1]))
break; /* in regexp, $ might be tail anchor */
}
+
+ /* backslashes */
if (*s == '\\' && s+1 < send) {
s++;
- if (*s && strchr(leave, *s)) {
+
+ /* some backslashes we leave behind */
+ if (*s && strchr(leaveit, *s)) {
*d++ = '\\';
*d++ = *s++;
continue;
}
+
+ /* deprecate \1 in strings and substitution replacements */
if (lex_inwhat == OP_SUBST && !lex_inpat &&
isDIGIT(*s) && *s != '0' && !isDIGIT(s[1]))
{
*--s = '$';
break;
}
+
+ /* string-change backslash escapes */
if (lex_inwhat != OP_TRANS && *s && strchr("lLuUEQ", *s)) {
--s;
break;
}
+
+ /* if we get here, it's either a quoted -, or a digit */
switch (*s) {
+
+ /* quoted - in transliterations */
case '-':
if (lex_inwhat == OP_TRANS) {
*d++ = *s++;
continue;
}
/* FALL THROUGH */
+ /* default action is to copy the quoted character */
default:
*d++ = *s++;
continue;
+
+ /* \132 indicates an octal constant */
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
*d++ = scan_oct(s, 3, &len);
s += len;
continue;
+
+ /* \x24 indicates a hex constant */
case 'x':
*d++ = scan_hex(++s, 2, &len);
s += len;
continue;
+
+ /* \c is a control character */
case 'c':
s++;
- *d = *s++;
- if (isLOWER(*d))
- *d = toUPPER(*d);
- *d++ ^= 64;
+ len = *s++;
+ *d++ = toCTRL(len);
continue;
+
+ /* printf-style backslashes, formfeeds, newlines, etc */
case 'b':
*d++ = '\b';
break;
case 'a':
*d++ = '\007';
break;
- }
+ } /* end switch */
+
s++;
continue;
- }
+ } /* end if (backslash) */
+
*d++ = *s++;
- }
+ } /* while loop to process each character */
+
+ /* terminate the string and set up the sv */
*d = '\0';
SvCUR_set(sv, d - SvPVX(sv));
SvPOK_on(sv);
+ /* shrink the sv if we allocated more than we used */
if (SvCUR(sv) + 5 < SvLEN(sv)) {
SvLEN_set(sv, SvCUR(sv) + 1);
Renew(SvPVX(sv), SvLEN(sv), char);
}
+
+ /* ??? */
if (s > bufptr)
yylval.opval = (OP*)newSVOP(OP_CONST, 0, sv);
else
/* This is the one truly awful dwimmer necessary to conflate C and sed. */
static int
-intuit_more(s)
-register char *s;
+intuit_more(register char *s)
{
if (lex_brackets)
return TRUE;
char seen[256];
unsigned char un_char = 0, last_un_char;
char *send = strchr(s,']');
- char tmpbuf[512];
+ char tmpbuf[sizeof tokenbuf * 4];
if (!send) /* has to be an expression */
return TRUE;
case '$':
weight -= seen[un_char] * 10;
if (isALNUM(s[1])) {
- scan_ident(s,send,tmpbuf,FALSE);
+ scan_ident(s, send, tmpbuf, sizeof tmpbuf, FALSE);
if ((int)strlen(tmpbuf) > 1 && gv_fetchpv(tmpbuf,FALSE, SVt_PV))
weight -= 100;
else
}
static int
-intuit_method(start,gv)
-char *start;
-GV *gv;
+intuit_method(char *start, GV *gv)
{
char *s = start + (*start == '$');
- char tmpbuf[1024];
+ char tmpbuf[sizeof tokenbuf];
STRLEN len;
GV* indirgv;
if (gv) {
+ CV *cv;
if (GvIO(gv))
return 0;
- if (!GvCV(gv))
+ if ((cv = GvCVu(gv))) {
+ char *proto = SvPVX(cv);
+ if (proto) {
+ if (*proto == ';')
+ proto++;
+ if (*proto == '*')
+ return 0;
+ }
+ } else
gv = 0;
}
- s = scan_word(s, tmpbuf, TRUE, &len);
+ s = scan_word(s, tmpbuf, sizeof tmpbuf, TRUE, &len);
if (*start == '$') {
if (gv || last_lop_op == OP_PRINT || isUPPER(*tokenbuf))
return 0;
return *s == '(' ? FUNCMETH : METHOD;
}
if (!keyword(tmpbuf, len)) {
- indirgv = gv_fetchpv(tmpbuf,FALSE, SVt_PVCV);
- if (indirgv && GvCV(indirgv))
+ if (len > 2 && tmpbuf[len - 2] == ':' && tmpbuf[len - 1] == ':') {
+ len -= 2;
+ tmpbuf[len] = '\0';
+ goto bare_package;
+ }
+ indirgv = gv_fetchpv(tmpbuf, FALSE, SVt_PVCV);
+ if (indirgv && GvCVu(indirgv))
return 0;
/* filehandle or package name makes it a method */
- if (!gv || GvIO(indirgv) || gv_stashpv(tmpbuf, FALSE)) {
+ if (!gv || GvIO(indirgv) || gv_stashpvn(tmpbuf, len, FALSE)) {
s = skipspace(s);
- nextval[nexttoke].opval =
- (OP*)newSVOP(OP_CONST, 0,
- newSVpv(tmpbuf,0));
- nextval[nexttoke].opval->op_private =
- OPpCONST_BARE;
+ if ((bufend - s) >= 2 && *s == '=' && *(s+1) == '>')
+ return 0; /* no assumptions -- "=>" quotes bearword */
+ bare_package:
+ nextval[nexttoke].opval = (OP*)newSVOP(OP_CONST, 0,
+ newSVpv(tmpbuf,0));
+ nextval[nexttoke].opval->op_private = OPpCONST_BARE;
expect = XTERM;
force_next(WORD);
bufptr = s;
}
static char*
-incl_perldb()
+incl_perldb(void)
{
if (perldb) {
- char *pdb = getenv("PERL5DB");
+ char *pdb = PerlEnv_getenv("PERL5DB");
if (pdb)
return pdb;
+ SETERRNO(0,SS$_NORMAL);
return "BEGIN { require 'perl5db.pl' }";
}
return "";
static int filter_debug = 0;
SV *
-filter_add(funcp, datasv)
- filter_t funcp;
- SV *datasv;
+filter_add(filter_t funcp, SV *datasv)
{
if (!funcp){ /* temporary handy debugging hack to be deleted */
filter_debug = atoi((char*)datasv);
if (!rsfp_filters)
rsfp_filters = newAV();
if (!datasv)
- datasv = newSV(0);
+ datasv = NEWSV(255,0);
if (!SvUPGRADE(datasv, SVt_PVIO))
die("Can't upgrade filter_add data to SVt_PVIO");
IoDIRP(datasv) = (DIR*)funcp; /* stash funcp into spare field */
if (filter_debug)
- warn("filter_add func %lx (%s)", funcp, SvPV(datasv,na));
+ warn("filter_add func %p (%s)", funcp, SvPV(datasv,na));
av_unshift(rsfp_filters, 1);
av_store(rsfp_filters, 0, datasv) ;
return(datasv);
/* Delete most recently added instance of this filter function. */
void
-filter_del(funcp)
- filter_t funcp;
+filter_del(filter_t funcp)
{
if (filter_debug)
- warn("filter_del func %lx", funcp);
- if (!rsfp_filters || AvFILL(rsfp_filters)<0)
+ warn("filter_del func %p", funcp);
+ if (!rsfp_filters || AvFILLp(rsfp_filters)<0)
return;
/* if filter is on top of stack (usual case) just pop it off */
- if (IoDIRP(FILTER_DATA(0)) == (void*)funcp){
- /* sv_free(av_pop(rsfp_filters)); */
- sv_free(av_shift(rsfp_filters));
+ if (IoDIRP(FILTER_DATA(AvFILLp(rsfp_filters))) == (void*)funcp){
+ sv_free(av_pop(rsfp_filters));
return;
}
/* Invoke the n'th filter function for the current rsfp. */
I32
-filter_read(idx, buf_sv, maxlen)
- int idx;
- SV *buf_sv;
- int maxlen; /* 0 = read one text line */
+filter_read(int idx, SV *buf_sv, int maxlen)
+
+
+ /* 0 = read one text line */
{
filter_t funcp;
SV *datasv = NULL;
if (!rsfp_filters)
return -1;
- if (idx > AvFILL(rsfp_filters)){ /* Any more filters? */
+ if (idx > AvFILLp(rsfp_filters)){ /* Any more filters? */
/* Provide a default input filter to make life easy. */
/* Note that we append to the line. This is handy. */
if (filter_debug)
/* ensure buf_sv is large enough */
SvGROW(buf_sv, old_len + maxlen) ;
- if ((len = fread(SvPVX(buf_sv) + old_len, 1, maxlen, rsfp)) <= 0){
- if (ferror(rsfp))
+ if ((len = PerlIO_read(rsfp, SvPVX(buf_sv) + old_len, maxlen)) <= 0){
+ if (PerlIO_error(rsfp))
return -1; /* error */
else
return 0 ; /* end of file */
} else {
/* Want a line */
if (sv_gets(buf_sv, rsfp, SvCUR(buf_sv)) == NULL) {
- if (ferror(rsfp))
+ if (PerlIO_error(rsfp))
return -1; /* error */
else
return 0 ; /* end of file */
/* Get function pointer hidden within datasv */
funcp = (filter_t)IoDIRP(datasv);
if (filter_debug)
- warn("filter_read %d: via function %lx (%s)\n",
+ warn("filter_read %d: via function %p (%s)\n",
idx, funcp, SvPV(datasv,na));
/* Call function. The function is expected to */
/* call "FILTER_READ(idx+1, buf_sv)" first. */
}
static char *
-filter_gets(sv,fp)
-register SV *sv;
-register FILE *fp;
+filter_gets(register SV *sv, register PerlIO *fp, STRLEN append)
{
+#ifdef WIN32FILTER
+ if (!rsfp_filters) {
+ filter_add(win32_textfilter,NULL);
+ }
+#endif
if (rsfp_filters) {
- SvCUR_set(sv, 0); /* start with empty line */
+ if (!append)
+ SvCUR_set(sv, 0); /* start with empty line */
if (FILTER_READ(0, sv, 0) > 0)
return ( SvPVX(sv) ) ;
else
return Nullch ;
}
else
- return (sv_gets(sv, fp, 0)) ;
-
+ return (sv_gets(sv, fp, append));
}
{ "OPERATOR", "TERM", "REF", "STATE", "BLOCK", "TERMBLOCK" };
#endif
-extern int yychar; /* last token */
+EXT int yychar; /* last token */
+
+/*
+ yylex
+
+ Works out what to call the token just pulled out of the input
+ stream. The yacc parser takes care of taking the ops we return and
+ stitching them into a tree.
+
+ Returns:
+ PRIVATEREF
+
+ Structure:
+ if read an identifier
+ if we're in a my declaration
+ croak if they tried to say my($foo::bar)
+ build the ops for a my() declaration
+ if it's an access to a my() variable
+ are we in a sort block?
+ croak if my($a); $a <=> $b
+ build ops for access to a my() variable
+ if in a dq string, and they've said @foo and we can't find @foo
+ croak
+ build ops for a bareword
+ if we already built the token before, use it.
+*/
int
-yylex()
+yylex(void)
{
+ dTHR;
register char *s;
register char *d;
register I32 tmp;
STRLEN len;
+ GV *gv = Nullgv;
+ GV **gvp = 0;
+
+ /* check if there's an identifier for us to look at */
+ if (pending_ident) {
+ /* pit holds the identifier we read and pending_ident is reset */
+ char pit = pending_ident;
+ pending_ident = 0;
+
+ /* if we're in a my(), we can't allow dynamics here.
+ $foo'bar has already been turned into $foo::bar, so
+ just check for colons.
+
+ if it's a legal name, the OP is a PADANY.
+ */
+ if (in_my) {
+ if (strchr(tokenbuf,':'))
+ croak(no_myglob,tokenbuf);
+
+ yylval.opval = newOP(OP_PADANY, 0);
+ yylval.opval->op_targ = pad_allocmy(tokenbuf);
+ return PRIVATEREF;
+ }
+
+ /*
+ build the ops for accesses to a my() variable.
+
+ Deny my($a) or my($b) in a sort block, *if* $a or $b is
+ then used in a comparison. This catches most, but not
+ all cases. For instance, it catches
+ sort { my($a); $a <=> $b }
+ but not
+ sort { my($a); $a < $b ? -1 : $a == $b ? 0 : 1; }
+ (although why you'd do that is anyone's guess).
+ */
+
+ if (!strchr(tokenbuf,':')) {
+#ifdef USE_THREADS
+ /* Check for single character per-thread SVs */
+ if (tokenbuf[0] == '$' && tokenbuf[2] == '\0'
+ && !isALPHA(tokenbuf[1]) /* Rule out obvious non-threadsvs */
+ && (tmp = find_threadsv(&tokenbuf[1])) != NOT_IN_PAD)
+ {
+ yylval.opval = newOP(OP_THREADSV, 0);
+ yylval.opval->op_targ = tmp;
+ return PRIVATEREF;
+ }
+#endif /* USE_THREADS */
+ if ((tmp = pad_findmy(tokenbuf)) != NOT_IN_PAD) {
+ /* if it's a sort block and they're naming $a or $b */
+ if (last_lop_op == OP_SORT &&
+ tokenbuf[0] == '$' &&
+ (tokenbuf[1] == 'a' || tokenbuf[1] == 'b')
+ && !tokenbuf[2])
+ {
+ for (d = in_eval ? oldoldbufptr : linestart;
+ d < bufend && *d != '\n';
+ d++)
+ {
+ if (strnEQ(d,"<=>",3) || strnEQ(d,"cmp",3)) {
+ croak("Can't use \"my %s\" in sort comparison",
+ tokenbuf);
+ }
+ }
+ }
+
+ yylval.opval = newOP(OP_PADANY, 0);
+ yylval.opval->op_targ = tmp;
+ return PRIVATEREF;
+ }
+ }
+
+ /*
+ Whine if they've said @foo in a doublequoted string,
+ and @foo isn't a variable we can find in the symbol
+ table.
+ */
+ if (pit == '@' && lex_state != LEX_NORMAL && !lex_brackets) {
+ GV *gv = gv_fetchpv(tokenbuf+1, FALSE, SVt_PVAV);
+ if (!gv || ((tokenbuf[0] == '@') ? !GvAV(gv) : !GvHV(gv)))
+ yyerror(form("In string, %s now must be written as \\%s",
+ tokenbuf, tokenbuf));
+ }
+
+ /* build ops for a bareword */
+ yylval.opval = (OP*)newSVOP(OP_CONST, 0, newSVpv(tokenbuf+1, 0));
+ yylval.opval->op_private = OPpCONST_ENTERED;
+ gv_fetchpv(tokenbuf+1, in_eval ? (GV_ADDMULTI | 8) : TRUE,
+ ((tokenbuf[0] == '$') ? SVt_PV
+ : (tokenbuf[0] == '@') ? SVt_PVAV
+ : SVt_PVHV));
+ return WORD;
+ }
+
+ /* no identifier pending identification */
switch (lex_state) {
#ifdef COMMENTARY
break;
#endif
+ /* when we're already built the next token, just pull it out the queue */
case LEX_KNOWNEXT:
nexttoke--;
yylval = nextval[nexttoke];
}
return(nexttype[nexttoke]);
+ /* interpolated case modifiers like \L \U, including \Q and \E.
+ when we get here, bufptr is at the \
+ */
case LEX_INTERPCASEMOD:
#ifdef DEBUGGING
if (bufptr != bufend && *bufptr != '\\')
croak("panic: INTERPCASEMOD");
#endif
- if (bufptr == bufend || bufptr[1] == 'E') {
+ /* handle \E or end of string */
+ if (bufptr == bufend || bufptr[1] == 'E') {
char oldmod;
+
+ /* if at a \E */
if (lex_casemods) {
oldmod = lex_casestack[--lex_casemods];
lex_casestack[lex_casemods] = '\0';
+
if (bufptr != bufend && strchr("LUQ", oldmod)) {
bufptr += 2;
lex_state = LEX_INTERPCONCAT;
return ')';
}
if (lex_casemods > 10) {
- char* newlb = (char*)realloc(lex_casestack, lex_casemods + 2);
+ char* newlb = Renew(lex_casestack, lex_casemods + 2, char);
if (newlb != lex_casestack) {
SAVEFREEPV(newlb);
lex_casestack = newlb;
return yylex();
}
+ case LEX_INTERPPUSH:
+ return sublex_push();
+
case LEX_INTERPSTART:
if (bufptr == bufend)
return sublex_done();
if (lex_dojoin) {
nextval[nexttoke].ival = 0;
force_next(',');
+#ifdef USE_THREADS
+ nextval[nexttoke].opval = newOP(OP_THREADSV, 0);
+ nextval[nexttoke].opval->op_targ = find_threadsv("\"");
+ force_next(PRIVATEREF);
+#else
force_ident("\"", '$');
+#endif /* USE_THREADS */
nextval[nexttoke].ival = 0;
force_next('$');
nextval[nexttoke].ival = 0;
s = bufptr;
Aop(OP_CONCAT);
}
- else
- return yylex();
- break;
+ return yylex();
case LEX_INTERPENDMAYBE:
if (intuit_more(bufptr)) {
oldoldbufptr = oldbufptr;
oldbufptr = s;
DEBUG_p( {
- fprintf(stderr,"### Tokener expecting %s at %s\n", exp_name[expect], s);
+ PerlIO_printf(PerlIO_stderr(), "### Tokener expecting %s at %s\n", exp_name[expect], s);
} )
retry:
switch (*s) {
default:
- warn("Unrecognized character \\%03o ignored", *s++ & 255);
- goto retry;
+ croak("Unrecognized character \\%03o", *s & 255);
case 4:
case 26:
goto fake_eof; /* emulate EOF on ^D or ^Z */
case 0:
if (!rsfp) {
+ last_uni = 0;
+ last_lop = 0;
if (lex_brackets)
yyerror("Missing right bracket");
TOKEN(0);
if (SvCUR(linestr))
sv_catpv(linestr,";");
if (preambleav){
- while(AvFILL(preambleav) >= 0) {
+ while(AvFILLp(preambleav) >= 0) {
SV *tmpsv = av_shift(preambleav);
sv_catsv(linestr, tmpsv);
sv_catpv(linestr, ";");
sv_catpv(linestr, "LINE: while (<>) {");
if (minus_l)
sv_catpv(linestr,"chomp;");
- if (minus_a){
- if (minus_F){
- char tmpbuf1[50];
- if ( splitstr[0] == '/' ||
- splitstr[0] == '\'' ||
- splitstr[0] == '"' )
- sprintf( tmpbuf1, "@F=split(%s);", splitstr );
- else
- sprintf( tmpbuf1, "@F=split('%s');", splitstr );
- sv_catpv(linestr,tmpbuf1);
+ if (minus_a) {
+ GV* gv = gv_fetchpv("::F", TRUE, SVt_PVAV);
+ if (gv)
+ GvIMPORTED_AV_on(gv);
+ if (minus_F) {
+ if (strchr("/'\"", *splitstr)
+ && strchr(splitstr + 1, *splitstr))
+ sv_catpvf(linestr, "@F=split(%s);", splitstr);
+ else {
+ char delim;
+ s = "'~#\200\1'"; /* surely one char is unused...*/
+ while (s[1] && strchr(splitstr, *s)) s++;
+ delim = *s;
+ sv_catpvf(linestr, "@F=split(%s%c",
+ "q" + (delim == '\''), delim);
+ for (s = splitstr; *s; s++) {
+ if (*s == '\\')
+ sv_catpvn(linestr, "\\", 1);
+ sv_catpvn(linestr, s, 1);
+ }
+ sv_catpvf(linestr, "%c);", delim);
+ }
}
else
sv_catpv(linestr,"@F=split(' ');");
}
}
sv_catpv(linestr, "\n");
- oldoldbufptr = oldbufptr = s = SvPVX(linestr);
+ oldoldbufptr = oldbufptr = s = linestart = SvPVX(linestr);
bufend = SvPVX(linestr) + SvCUR(linestr);
- if (perldb && curstash != debstash) {
+ if (PERLDB_LINE && curstash != debstash) {
SV *sv = NEWSV(85,0);
sv_upgrade(sv, SVt_PVMG);
goto retry;
}
do {
- if ((s = filter_gets(linestr, rsfp)) == Nullch) {
+ if ((s = filter_gets(linestr, rsfp, 0)) == Nullch) {
fake_eof:
if (rsfp) {
if (preprocess && !in_eval)
- (void)my_pclose(rsfp);
- else if ((FILE*)rsfp == stdin)
- clearerr(stdin);
+ (void)PerlProc_pclose(rsfp);
+ else if ((PerlIO *)rsfp == PerlIO_stdin())
+ PerlIO_clearerr(rsfp);
else
- (void)fclose(rsfp);
+ (void)PerlIO_close(rsfp);
+ if (e_fp == rsfp)
+ e_fp = Nullfp;
rsfp = Nullfp;
}
if (!in_eval && (minus_n || minus_p)) {
sv_setpv(linestr,minus_p ? ";}continue{print" : "");
sv_catpv(linestr,";}");
- oldoldbufptr = oldbufptr = s = SvPVX(linestr);
+ oldoldbufptr = oldbufptr = s = linestart = SvPVX(linestr);
bufend = SvPVX(linestr) + SvCUR(linestr);
minus_n = minus_p = 0;
goto retry;
}
- oldoldbufptr = oldbufptr = s = SvPVX(linestr);
+ oldoldbufptr = oldbufptr = s = linestart = SvPVX(linestr);
sv_setpv(linestr,"");
TOKEN(';'); /* not infinite loop because rsfp is NULL now */
}
/* Incest with pod. */
if (*s == '=' && strnEQ(s, "=cut", 4)) {
sv_setpv(linestr, "");
- oldoldbufptr = oldbufptr = s = SvPVX(linestr);
+ oldoldbufptr = oldbufptr = s = linestart = SvPVX(linestr);
bufend = SvPVX(linestr) + SvCUR(linestr);
doextract = FALSE;
}
}
incline(s);
} while (doextract);
- oldoldbufptr = oldbufptr = bufptr = s;
- if (perldb && curstash != debstash) {
+ oldoldbufptr = oldbufptr = bufptr = linestart = s;
+ if (PERLDB_LINE && curstash != debstash) {
SV *sv = NEWSV(85,0);
sv_upgrade(sv, SVt_PVMG);
s++;
if (*s == ':' && s[1] != ':') /* for csh execing sh scripts */
s++;
- if (!in_eval && *s == '#' && s[1] == '!') {
+ d = Nullch;
+ if (!in_eval) {
+ if (*s == '#' && *(s+1) == '!')
+ d = s + 2;
+#ifdef ALTERNATE_SHEBANG
+ else {
+ static char as[] = ALTERNATE_SHEBANG;
+ if (*s == as[0] && strnEQ(s, as, sizeof(as) - 1))
+ d = s + (sizeof(as) - 1);
+ }
+#endif /* ALTERNATE_SHEBANG */
+ }
+ if (d) {
+ char *ipath;
+ char *ipathend;
+
+ while (isSPACE(*d))
+ d++;
+ ipath = d;
+ while (*d && !isSPACE(*d))
+ d++;
+ ipathend = d;
+
+#ifdef ARG_ZERO_IS_SCRIPT
+ if (ipathend > ipath) {
+ /*
+ * HP-UX (at least) sets argv[0] to the script name,
+ * which makes $^X incorrect. And Digital UNIX and Linux,
+ * at least, set argv[0] to the basename of the Perl
+ * interpreter. So, having found "#!", we'll set it right.
+ */
+ SV *x = GvSV(gv_fetchpv("\030", TRUE, SVt_PV));
+ assert(SvPOK(x) || SvGMAGICAL(x));
+ if (sv_eq(x, GvSV(curcop->cop_filegv))) {
+ sv_setpvn(x, ipath, ipathend - ipath);
+ SvSETMAGIC(x);
+ }
+ TAINT_NOT; /* $^X is always tainted, but that's OK */
+ }
+#endif /* ARG_ZERO_IS_SCRIPT */
+
+ /*
+ * Look for options.
+ */
d = instr(s,"perl -");
if (!d)
d = instr(s,"perl");
+#ifdef ALTERNATE_SHEBANG
+ /*
+ * If the ALTERNATE_SHEBANG on this system starts with a
+ * character that can be part of a Perl expression, then if
+ * we see it but not "perl", we're probably looking at the
+ * start of Perl code, not a request to hand off to some
+ * other interpreter. Similarly, if "perl" is there, but
+ * not in the first 'word' of the line, we assume the line
+ * contains the start of the Perl program.
+ */
+ if (d && *s != '#') {
+ char *c = ipath;
+ while (*c && !strchr("; \t\r\n\f\v#", *c))
+ c++;
+ if (c < d)
+ d = Nullch; /* "perl" not in first word; ignore */
+ else
+ *s = '#'; /* Don't try to parse shebang line */
+ }
+#endif /* ALTERNATE_SHEBANG */
if (!d &&
+ *s == '#' &&
+ ipathend > ipath &&
!minus_c &&
!instr(s,"indir") &&
instr(origargv[0],"perl"))
{
char **newargv;
- char *cmd;
- s += 2;
- if (*s == ' ')
- s++;
- cmd = s;
- while (s < bufend && !isSPACE(*s))
- s++;
- *s++ = '\0';
+ *ipathend = '\0';
+ s = ipathend + 1;
while (s < bufend && isSPACE(*s))
s++;
if (s < bufend) {
}
else
newargv = origargv;
- newargv[0] = cmd;
- execv(cmd,newargv);
- croak("Can't exec %s", cmd);
+ newargv[0] = ipath;
+ execv(ipath, newargv);
+ croak("Can't exec %s", ipath);
}
if (d) {
- int oldpdb = perldb;
- int oldn = minus_n;
- int oldp = minus_p;
+ U32 oldpdb = perldb;
+ bool oldn = minus_n;
+ bool oldp = minus_p;
while (*d && !isSPACE(*d)) d++;
- while (*d == ' ') d++;
+ while (*d == ' ' || *d == '\t') d++;
if (*d++ == '-') {
- while (d = moreswitches(d)) ;
- if (perldb && !oldpdb ||
+ do {
+ if (*d == 'M' || *d == 'm') {
+ char *m = d;
+ while (*d && !isSPACE(*d)) d++;
+ croak("Too late for \"-%.*s\" option",
+ (int)(d - m), m);
+ }
+ d = moreswitches(d);
+ } while (d);
+ if (PERLDB_LINE && !oldpdb ||
( minus_n || minus_p ) && !(oldn || oldp) )
/* if we have already added "LINE: while (<>) {",
we must not do it again */
{
sv_setpv(linestr, "");
- oldoldbufptr = oldbufptr = s = SvPVX(linestr);
+ oldoldbufptr = oldbufptr = s = linestart = SvPVX(linestr);
bufend = SvPVX(linestr) + SvCUR(linestr);
preambled = FALSE;
- if (perldb)
+ if (PERLDB_LINE)
(void)gv_fetchfile(origfilename);
goto retry;
}
return yylex();
}
goto retry;
- case ' ': case '\t': case '\f': case '\r': case 013:
+ case '\r':
+#ifndef WIN32CHEAT
+ warn("Illegal character \\%03o (carriage return)", '\r');
+ croak(
+ "(Maybe you didn't strip carriage returns after a network transfer?)\n");
+#endif
+ case ' ': case '\t': case '\f': case 013:
s++;
goto retry;
case '#':
s++;
if (strnEQ(s,"=>",2)) {
- if (dowarn)
- warn("Ambiguous use of -%c => resolved to \"-%c\" =>",
- tmp, tmp);
s = force_word(bufptr,WORD,FALSE,FALSE,FALSE);
OPERATOR('-'); /* unary minus */
}
case 'A': gv_fetchpv("\024",TRUE, SVt_PV); FTST(OP_FTATIME);
case 'C': gv_fetchpv("\024",TRUE, SVt_PV); FTST(OP_FTCTIME);
default:
- croak("Unrecognized file test: -%c", tmp);
+ croak("Unrecognized file test: -%c", (int)tmp);
break;
}
}
case '*':
if (expect != XOPERATOR) {
- s = scan_ident(s, bufend, tokenbuf, TRUE);
+ s = scan_ident(s, bufend, tokenbuf, sizeof tokenbuf, TRUE);
expect = XOPERATOR;
force_ident(tokenbuf, '*');
if (!*tokenbuf)
Mop(OP_MULTIPLY);
case '%':
- if (expect != XOPERATOR) {
- s = scan_ident(s, bufend, tokenbuf + 1, TRUE);
- if (tokenbuf[1]) {
- expect = XOPERATOR;
- tokenbuf[0] = '%';
- if (in_my) {
- if (strchr(tokenbuf,':'))
- croak(no_myglob,tokenbuf);
- nextval[nexttoke].opval = newOP(OP_PADANY, 0);
- nextval[nexttoke].opval->op_targ = pad_allocmy(tokenbuf);
- force_next(PRIVATEREF);
- TERM('%');
- }
- if (!strchr(tokenbuf,':')) {
- if (tmp = pad_findmy(tokenbuf)) {
- nextval[nexttoke].opval = newOP(OP_PADANY, 0);
- nextval[nexttoke].opval->op_targ = tmp;
- force_next(PRIVATEREF);
- TERM('%');
- }
- }
- force_ident(tokenbuf + 1, *tokenbuf);
- }
- else
- PREREF('%');
- TERM('%');
+ if (expect == XOPERATOR) {
+ ++s;
+ Mop(OP_MODULO);
+ }
+ tokenbuf[0] = '%';
+ s = scan_ident(s, bufend, tokenbuf + 1, sizeof tokenbuf - 1, TRUE);
+ if (!tokenbuf[1]) {
+ if (s == bufend)
+ yyerror("Final % should be \\% or %name");
+ PREREF('%');
}
- ++s;
- Mop(OP_MODULO);
+ pending_ident = '%';
+ TERM('%');
case '^':
s++;
leftbracket:
s++;
if (lex_brackets > 100) {
- char* newlb = (char*)realloc(lex_brackstack, lex_brackets + 1);
+ char* newlb = Renew(lex_brackstack, lex_brackets + 1, char);
if (newlb != lex_brackstack) {
SAVEFREEPV(newlb);
lex_brackstack = newlb;
else
lex_brackstack[lex_brackets++] = XOPERATOR;
OPERATOR(HASHBRACK);
- break;
case XOPERATOR:
while (s < bufend && (*s == ' ' || *s == '\t'))
s++;
- if (s < bufend && isALPHA(*s)) {
- d = scan_word(s, tokenbuf, FALSE, &len);
+ d = s;
+ tokenbuf[0] = '\0';
+ if (d < bufend && *d == '-') {
+ tokenbuf[0] = '-';
+ d++;
+ while (d < bufend && (*d == ' ' || *d == '\t'))
+ d++;
+ }
+ if (d < bufend && isIDFIRST(*d)) {
+ d = scan_word(d, tokenbuf + 1, sizeof tokenbuf - 1,
+ FALSE, &len);
while (d < bufend && (*d == ' ' || *d == '\t'))
d++;
if (*d == '}') {
- if (dowarn &&
- (keyword(tokenbuf, len) ||
- perl_get_cv(tokenbuf, FALSE) ))
- warn("Ambiguous use of {%s} resolved to {\"%s\"}",
- tokenbuf, tokenbuf);
- s = force_word(s,WORD,FALSE,TRUE,FALSE);
+ char minus = (tokenbuf[0] == '-');
+ s = force_word(s + minus, WORD, FALSE, TRUE, FALSE);
+ if (minus)
+ force_next('-');
}
}
/* FALL THROUGH */
else
lex_brackstack[lex_brackets++] = XOPERATOR;
s = skipspace(s);
- if (*s == '}')
+ if (*s == '}') {
+ if (expect == XSTATE) {
+ lex_brackstack[lex_brackets-1] = XSTATE;
+ break;
+ }
OPERATOR(HASHBRACK);
- if (isALPHA(*s)) {
- for (t = s; t < bufend && isALNUM(*t); t++) ;
}
- else if (*s == '\'' || *s == '"') {
- t = strchr(s+1,*s);
- if (!t++)
- t = s;
+ /* This hack serves to disambiguate a pair of curlies
+ * as being a block or an anon hash. Normally, expectation
+ * determines that, but in cases where we're not in a
+ * position to expect anything in particular (like inside
+ * eval"") we have to resolve the ambiguity. This code
+ * covers the case where the first term in the curlies is a
+ * quoted string. Most other cases need to be explicitly
+ * disambiguated by prepending a `+' before the opening
+ * curly in order to force resolution as an anon hash.
+ *
+ * XXX should probably propagate the outer expectation
+ * into eval"" to rely less on this hack, but that could
+ * potentially break current behavior of eval"".
+ * GSAR 97-07-21
+ */
+ t = s;
+ if (*s == '\'' || *s == '"' || *s == '`') {
+ /* common case: get past first string, handling escapes */
+ for (t++; t < bufend && *t != *s;)
+ if (*t++ == '\\' && (*t == '\\' || *t == *s))
+ t++;
+ t++;
+ }
+ else if (*s == 'q') {
+ if (++t < bufend
+ && (!isALNUM(*t)
+ || ((*t == 'q' || *t == 'x') && ++t < bufend
+ && !isALNUM(*t)))) {
+ char *tmps;
+ char open, close, term;
+ I32 brackets = 1;
+
+ while (t < bufend && isSPACE(*t))
+ t++;
+ term = *t;
+ open = term;
+ if (term && (tmps = strchr("([{< )]}> )]}>",term)))
+ term = tmps[5];
+ close = term;
+ if (open == close)
+ for (t++; t < bufend; t++) {
+ if (*t == '\\' && t+1 < bufend && open != '\\')
+ t++;
+ else if (*t == open)
+ break;
+ }
+ else
+ for (t++; t < bufend; t++) {
+ if (*t == '\\' && t+1 < bufend)
+ t++;
+ else if (*t == close && --brackets <= 0)
+ break;
+ else if (*t == open)
+ brackets++;
+ }
+ }
+ t++;
+ }
+ else if (isALPHA(*s)) {
+ for (t++; t < bufend && isALNUM(*t); t++) ;
}
- else
- t = s;
while (t < bufend && isSPACE(*t))
t++;
- if ((*t == ',' && !isLOWER(*s)) || (*t == '=' && t[1] == '>'))
+ /* if comma follows first term, call it an anon hash */
+ /* XXX it could be a comma expression with loop modifiers */
+ if (t < bufend && ((*t == ',' && (*s == 'q' || !isLOWER(*s)))
+ || (*t == '=' && t[1] == '>')))
OPERATOR(HASHBRACK);
if (expect == XREF)
expect = XTERM;
bufptr = s;
return yylex(); /* ignore fake brackets */
}
- if (*s != '[' && *s != '{' && (*s != '-' || s[1] != '>'))
+ if (*s == '-' && s[1] == '>')
+ lex_state = LEX_INTERPENDMAYBE;
+ else if (*s != '[' && *s != '{')
lex_state = LEX_INTERPEND;
}
}
AOPERATOR(ANDAND);
s--;
if (expect == XOPERATOR) {
- if (dowarn && isALPHA(*s) && bufptr == SvPVX(linestr)) {
+ if (dowarn && isALPHA(*s) && bufptr == linestart) {
curcop->cop_line--;
warn(warn_nosemi);
curcop->cop_line++;
BAop(OP_BIT_AND);
}
- s = scan_ident(s-1, bufend, tokenbuf, TRUE);
+ s = scan_ident(s - 1, bufend, tokenbuf, sizeof tokenbuf, TRUE);
if (*tokenbuf) {
expect = XOPERATOR;
force_ident(tokenbuf, '&');
if (tmp == '~')
PMop(OP_MATCH);
if (dowarn && tmp && isSPACE(*s) && strchr("+-*/%.^&|<",tmp))
- warn("Reversed %c= operator",tmp);
+ warn("Reversed %c= operator",(int)tmp);
s--;
if (expect == XSTATE && isALPHA(tmp) &&
- (s == SvPVX(linestr)+1 || s[-2] == '\n') )
+ (s == linestart+1 || s[-2] == '\n') )
{
if (in_eval && !rsfp) {
d = bufend;
Rop(OP_GT);
case '$':
- if (s[1] == '#' && (isALPHA(s[2]) || strchr("_{$:", s[2]))) {
- s = scan_ident(s+1, bufend, tokenbuf+1, FALSE);
- if (expect == XOPERATOR) {
- if (lex_formbrack && lex_brackets == lex_formbrack) {
- expect = XTERM;
- depcom();
- return ','; /* grandfather non-comma-format format */
- }
- else
- no_op("Array length",s);
- }
- else if (!tokenbuf[1])
- PREREF(DOLSHARP);
- if (!strchr(tokenbuf+1,':')) {
- tokenbuf[0] = '@';
- if (tmp = pad_findmy(tokenbuf)) {
- nextval[nexttoke].opval = newOP(OP_PADANY, 0);
- nextval[nexttoke].opval->op_targ = tmp;
- expect = XOPERATOR;
- force_next(PRIVATEREF);
- TOKEN(DOLSHARP);
- }
- }
- expect = XOPERATOR;
- force_ident(tokenbuf+1, *tokenbuf);
- TOKEN(DOLSHARP);
- }
- s = scan_ident(s, bufend, tokenbuf+1, FALSE);
+ CLINE;
+
if (expect == XOPERATOR) {
if (lex_formbrack && lex_brackets == lex_formbrack) {
expect = XTERM;
depcom();
- return ','; /* grandfather non-comma-format format */
+ return ','; /* grandfather non-comma-format format */
}
- else
- no_op("Scalar",s);
}
- if (tokenbuf[1]) {
- expectation oldexpect = expect;
- /* This kludge not intended to be bulletproof. */
- if (tokenbuf[1] == '[' && !tokenbuf[2]) {
- yylval.opval = newSVOP(OP_CONST, 0,
- newSViv((IV)compiling.cop_arybase));
- yylval.opval->op_private = OPpCONST_ARYBASE;
- TERM(THING);
- }
- tokenbuf[0] = '$';
- if (dowarn) {
- char *t;
- if (*s == '[' && oldexpect != XREF) {
- for (t = s+1; isSPACE(*t) || isALNUM(*t) || *t == '$'; t++) ;
+ if (s[1] == '#' && (isALPHA(s[2]) || strchr("_{$:", s[2]))) {
+ if (expect == XOPERATOR)
+ no_op("Array length", bufptr);
+ tokenbuf[0] = '@';
+ s = scan_ident(s + 1, bufend, tokenbuf + 1, sizeof tokenbuf - 1,
+ FALSE);
+ if (!tokenbuf[1])
+ PREREF(DOLSHARP);
+ expect = XOPERATOR;
+ pending_ident = '#';
+ TOKEN(DOLSHARP);
+ }
+
+ if (expect == XOPERATOR)
+ no_op("Scalar", bufptr);
+ tokenbuf[0] = '$';
+ s = scan_ident(s, bufend, tokenbuf + 1, sizeof tokenbuf - 1, FALSE);
+ if (!tokenbuf[1]) {
+ if (s == bufend)
+ yyerror("Final $ should be \\$ or $name");
+ PREREF('$');
+ }
+
+ /* This kludge not intended to be bulletproof. */
+ if (tokenbuf[1] == '[' && !tokenbuf[2]) {
+ yylval.opval = newSVOP(OP_CONST, 0,
+ newSViv((IV)compiling.cop_arybase));
+ yylval.opval->op_private = OPpCONST_ARYBASE;
+ TERM(THING);
+ }
+
+ d = s;
+ if (lex_state == LEX_NORMAL)
+ s = skipspace(s);
+
+ if ((expect != XREF || oldoldbufptr == last_lop) && intuit_more(s)) {
+ char *t;
+ if (*s == '[') {
+ tokenbuf[0] = '@';
+ if (dowarn) {
+ for(t = s + 1;
+ isSPACE(*t) || isALNUM(*t) || *t == '$';
+ t++) ;
if (*t++ == ',') {
bufptr = skipspace(bufptr);
- while (t < bufend && *t != ']') t++;
+ while (t < bufend && *t != ']')
+ t++;
warn("Multidimensional syntax %.*s not supported",
- t-bufptr+1, bufptr);
+ (t - bufptr) + 1, bufptr);
}
}
- if (*s == '{' && strEQ(tokenbuf, "$SIG") &&
- (t = strchr(s,'}')) && (t = strchr(t,'='))) {
- char tmpbuf[1024];
+ }
+ else if (*s == '{') {
+ tokenbuf[0] = '%';
+ if (dowarn && strEQ(tokenbuf+1, "SIG") &&
+ (t = strchr(s, '}')) && (t = strchr(t, '=')))
+ {
+ char tmpbuf[sizeof tokenbuf];
STRLEN len;
for (t++; isSPACE(*t); t++) ;
if (isIDFIRST(*t)) {
- t = scan_word(t, tmpbuf, TRUE, &len);
+ t = scan_word(t, tmpbuf, sizeof tmpbuf, TRUE, &len);
if (*t != '(' && perl_get_cv(tmpbuf, FALSE))
warn("You need to quote \"%s\"", tmpbuf);
}
}
}
- expect = XOPERATOR;
- if (lex_state == LEX_NORMAL && isSPACE(*s)) {
- bool islop = (last_lop == oldoldbufptr);
- s = skipspace(s);
- if (!islop || last_lop_op == OP_GREPSTART)
- expect = XOPERATOR;
- else if (strchr("$@\"'`q", *s))
- expect = XTERM; /* e.g. print $fh "foo" */
- else if (strchr("&*<%", *s) && isIDFIRST(s[1]))
- expect = XTERM; /* e.g. print $fh &sub */
- else if (isDIGIT(*s))
- expect = XTERM; /* e.g. print $fh 3 */
- else if (*s == '.' && isDIGIT(s[1]))
- expect = XTERM; /* e.g. print $fh .3 */
- else if (strchr("/?-+", *s) && !isSPACE(s[1]))
- expect = XTERM; /* e.g. print $fh -1 */
- else if (*s == '<' && s[1] == '<' && !isSPACE(s[2]))
- expect = XTERM; /* print $fh <<"EOF" */
- }
- if (in_my) {
- if (strchr(tokenbuf,':'))
- croak(no_myglob,tokenbuf);
- nextval[nexttoke].opval = newOP(OP_PADANY, 0);
- nextval[nexttoke].opval->op_targ = pad_allocmy(tokenbuf);
- force_next(PRIVATEREF);
- }
- else if (!strchr(tokenbuf,':')) {
- if (oldexpect != XREF || oldoldbufptr == last_lop) {
- if (intuit_more(s)) {
- if (*s == '[')
- tokenbuf[0] = '@';
- else if (*s == '{')
- tokenbuf[0] = '%';
+ }
+
+ expect = XOPERATOR;
+ if (lex_state == LEX_NORMAL && isSPACE(*d)) {
+ bool islop = (last_lop == oldoldbufptr);
+ if (!islop || last_lop_op == OP_GREPSTART)
+ expect = XOPERATOR;
+ else if (strchr("$@\"'`q", *s))
+ expect = XTERM; /* e.g. print $fh "foo" */
+ else if (strchr("&*<%", *s) && isIDFIRST(s[1]))
+ expect = XTERM; /* e.g. print $fh &sub */
+ else if (isIDFIRST(*s)) {
+ char tmpbuf[sizeof tokenbuf];
+ scan_word(s, tmpbuf, sizeof tmpbuf, TRUE, &len);
+ if (tmp = keyword(tmpbuf, len)) {
+ /* binary operators exclude handle interpretations */
+ switch (tmp) {
+ case -KEY_x:
+ case -KEY_eq:
+ case -KEY_ne:
+ case -KEY_gt:
+ case -KEY_lt:
+ case -KEY_ge:
+ case -KEY_le:
+ case -KEY_cmp:
+ break;
+ default:
+ expect = XTERM; /* e.g. print $fh length() */
+ break;
}
}
- if (tmp = pad_findmy(tokenbuf)) {
- if (!tokenbuf[2] && *tokenbuf =='$' &&
- tokenbuf[1] <= 'b' && tokenbuf[1] >= 'a')
- {
- for (d = in_eval ? oldoldbufptr : SvPVX(linestr);
- d < bufend && *d != '\n';
- d++)
- {
- if (strnEQ(d,"<=>",3) || strnEQ(d,"cmp",3)) {
- croak("Can't use \"my %s\" in sort comparison",
- tokenbuf);
- }
- }
- }
- nextval[nexttoke].opval = newOP(OP_PADANY, 0);
- nextval[nexttoke].opval->op_targ = tmp;
- force_next(PRIVATEREF);
+ else {
+ GV *gv = gv_fetchpv(tmpbuf, FALSE, SVt_PVCV);
+ if (gv && GvCVu(gv))
+ expect = XTERM; /* e.g. print $fh subr() */
}
- else
- force_ident(tokenbuf+1, *tokenbuf);
}
- else
- force_ident(tokenbuf+1, *tokenbuf);
- }
- else {
- if (s == bufend)
- yyerror("Final $ should be \\$ or $name");
- PREREF('$');
- }
+ else if (isDIGIT(*s))
+ expect = XTERM; /* e.g. print $fh 3 */
+ else if (*s == '.' && isDIGIT(s[1]))
+ expect = XTERM; /* e.g. print $fh .3 */
+ else if (strchr("/?-+", *s) && !isSPACE(s[1]))
+ expect = XTERM; /* e.g. print $fh -1 */
+ else if (*s == '<' && s[1] == '<' && !isSPACE(s[2]))
+ expect = XTERM; /* print $fh <<"EOF" */
+ }
+ pending_ident = '$';
TOKEN('$');
case '@':
- s = scan_ident(s, bufend, tokenbuf+1, FALSE);
if (expect == XOPERATOR)
- no_op("Array",s);
- if (tokenbuf[1]) {
- GV* gv;
-
- tokenbuf[0] = '@';
- expect = XOPERATOR;
- if (in_my) {
- if (strchr(tokenbuf,':'))
- croak(no_myglob,tokenbuf);
- nextval[nexttoke].opval = newOP(OP_PADANY, 0);
- nextval[nexttoke].opval->op_targ = pad_allocmy(tokenbuf);
- force_next(PRIVATEREF);
- TERM('@');
- }
- else if (!strchr(tokenbuf,':')) {
- if (intuit_more(s)) {
- if (*s == '{')
- tokenbuf[0] = '%';
- }
- if (tmp = pad_findmy(tokenbuf)) {
- nextval[nexttoke].opval = newOP(OP_PADANY, 0);
- nextval[nexttoke].opval->op_targ = tmp;
- force_next(PRIVATEREF);
- TERM('@');
- }
- }
-
- /* Force them to make up their mind on "@foo". */
- if (lex_state != LEX_NORMAL && !lex_brackets &&
- ( !(gv = gv_fetchpv(tokenbuf+1, FALSE, SVt_PVAV)) ||
- (*tokenbuf == '@'
- ? !GvAV(gv)
- : !GvHV(gv) )))
- {
- char tmpbuf[1024];
- sprintf(tmpbuf, "Literal @%s now requires backslash",tokenbuf+1);
- yyerror(tmpbuf);
- }
+ no_op("Array", s);
+ tokenbuf[0] = '@';
+ s = scan_ident(s, bufend, tokenbuf + 1, sizeof tokenbuf - 1, FALSE);
+ if (!tokenbuf[1]) {
+ if (s == bufend)
+ yyerror("Final @ should be \\@ or @name");
+ PREREF('@');
+ }
+ if (lex_state == LEX_NORMAL)
+ s = skipspace(s);
+ if ((expect != XREF || oldoldbufptr == last_lop) && intuit_more(s)) {
+ if (*s == '{')
+ tokenbuf[0] = '%';
/* Warn about @ where they meant $. */
if (dowarn) {
}
}
}
- force_ident(tokenbuf+1, *tokenbuf);
- }
- else {
- if (s == bufend)
- yyerror("Final @ should be \\@ or @name");
- PREREF('@');
}
+ pending_ident = '@';
TERM('@');
case '/': /* may either be division or pattern */
case '?': /* may either be conditional or pattern */
if (expect != XOPERATOR) {
- check_uni();
+ /* Disable warning on "study /blah/" */
+ if (oldoldbufptr == last_uni
+ && (*last_uni != 's' || s - last_uni < 5
+ || memNE(last_uni, "study", 5) || isALNUM(last_uni[5])))
+ check_uni();
s = scan_pat(s);
TERM(sublex_start());
}
case '.':
if (lex_formbrack && lex_brackets == lex_formbrack && s[1] == '\n' &&
- (s == SvPVX(linestr) || s[-1] == '\n') ) {
+ (s == linestart || s[-1] == '\n') ) {
lex_formbrack = 0;
expect = XSTATE;
goto rightbracket;
case 'y': case 'Y':
case 'z': case 'Z':
- keylookup:
+ keylookup: {
+ gv = Nullgv;
+ gvp = 0;
+
bufptr = s;
- s = scan_word(s, tokenbuf, FALSE, &len);
-
- if (*s == ':' && s[1] == ':' && strNE(tokenbuf, "CORE"))
+ s = scan_word(s, tokenbuf, sizeof tokenbuf, FALSE, &len);
+
+ /* Some keywords can be followed by any delimiter, including ':' */
+ tmp = (len == 1 && strchr("msyq", tokenbuf[0]) ||
+ len == 2 && ((tokenbuf[0] == 't' && tokenbuf[1] == 'r') ||
+ (tokenbuf[0] == 'q' &&
+ strchr("qwx", tokenbuf[1]))));
+
+ /* x::* is just a word, unless x is "CORE" */
+ if (!tmp && *s == ':' && s[1] == ':' && strNE(tokenbuf, "CORE"))
goto just_a_word;
+ d = s;
+ while (d < bufend && isSPACE(*d))
+ d++; /* no comments skipped here, or s### is misparsed */
+
+ /* Is this a label? */
+ if (!tmp && expect == XSTATE
+ && d < bufend && *d == ':' && *(d + 1) != ':') {
+ s = d + 1;
+ yylval.pval = savepv(tokenbuf);
+ CLINE;
+ TOKEN(LABEL);
+ }
+
+ /* Check for keywords */
tmp = keyword(tokenbuf, len);
/* Is this a word before a => operator? */
- d = s;
- while (d < bufend && (*d == ' ' || *d == '\t'))
- d++; /* no comments skipped here, or s### is misparsed */
if (strnEQ(d,"=>",2)) {
CLINE;
- if (dowarn && (tmp || perl_get_cv(tokenbuf, FALSE)))
- warn("Ambiguous use of %s => resolved to \"%s\" =>",
- tokenbuf, tokenbuf);
yylval.opval = (OP*)newSVOP(OP_CONST, 0, newSVpv(tokenbuf,0));
yylval.opval->op_private = OPpCONST_BARE;
TERM(WORD);
}
if (tmp < 0) { /* second-class keyword? */
- GV* gv;
- if (expect != XOPERATOR &&
- (*s != ':' || s[1] != ':') &&
- (gv = gv_fetchpv(tokenbuf, FALSE, SVt_PVCV)) &&
- GvIMPORTED_CV(gv))
+ if (expect != XOPERATOR && (*s != ':' || s[1] != ':') &&
+ (((gv = gv_fetchpv(tokenbuf, FALSE, SVt_PVCV)) &&
+ GvCVu(gv) && GvIMPORTED_CV(gv)) ||
+ ((gvp = (GV**)hv_fetch(globalstash,tokenbuf,len,FALSE)) &&
+ (gv = *gvp) != (GV*)&sv_undef &&
+ GvCVu(gv) && GvIMPORTED_CV(gv))))
{
- tmp = 0;
+ tmp = 0; /* overridden by importation */
+ }
+ else if (gv && !gvp
+ && -tmp==KEY_lock /* XXX generalizable kludge */
+ && !hv_fetch(GvHVn(incgv), "Thread.pm", 9, FALSE))
+ {
+ tmp = 0; /* any sub overrides "weak" keyword */
+ }
+ else {
+ tmp = -tmp; gv = Nullgv; gvp = 0;
}
- else
- tmp = -tmp;
}
reserved_word:
default: /* not a keyword */
just_a_word: {
- GV *gv;
+ SV *sv;
char lastchar = (bufptr == oldoldbufptr ? 0 : bufptr[-1]);
/* Get the rest if it looks like a package qualifier */
if (*s == '\'' || *s == ':' && s[1] == ':') {
- s = scan_word(s, tokenbuf + len, TRUE, &len);
- if (!len)
- croak("Bad name after %s::", tokenbuf);
+ STRLEN morelen;
+ s = scan_word(s, tokenbuf + len, sizeof tokenbuf - len,
+ TRUE, &morelen);
+ if (!morelen)
+ croak("Bad name after %s%s", tokenbuf,
+ *s == '\'' ? "'" : "::");
+ len += morelen;
}
- /* Do special processing at start of statement. */
-
- if (expect == XSTATE) {
- while (isSPACE(*s)) s++;
- if (*s == ':') { /* It's a label. */
- yylval.pval = savepv(tokenbuf);
- s++;
- CLINE;
- TOKEN(LABEL);
- }
- }
- else if (expect == XOPERATOR) {
- if (bufptr == SvPVX(linestr)) {
+ if (expect == XOPERATOR) {
+ if (bufptr == linestart) {
curcop->cop_line--;
warn(warn_nosemi);
curcop->cop_line++;
}
else
- no_op("Bare word",s);
+ no_op("Bareword",s);
+ }
+
+ /* Look for a subroutine with this name in current package,
+ unless name is "Foo::", in which case Foo is a bearword
+ (and a package name). */
+
+ if (len > 2 &&
+ tokenbuf[len - 2] == ':' && tokenbuf[len - 1] == ':')
+ {
+ if (dowarn && ! gv_fetchpv(tokenbuf, FALSE, SVt_PVHV))
+ warn("Bareword \"%s\" refers to nonexistent package",
+ tokenbuf);
+ len -= 2;
+ tokenbuf[len] = '\0';
+ gv = Nullgv;
+ gvp = 0;
+ }
+ else {
+ len = 0;
+ if (!gv)
+ gv = gv_fetchpv(tokenbuf, FALSE, SVt_PVCV);
}
- /* Look for a subroutine with this name in current package. */
+ /* if we saw a global override before, get the right name */
- gv = gv_fetchpv(tokenbuf,FALSE, SVt_PVCV);
+ if (gvp) {
+ sv = newSVpv("CORE::GLOBAL::",14);
+ sv_catpv(sv,tokenbuf);
+ }
+ else
+ sv = newSVpv(tokenbuf,0);
/* Presume this is going to be a bareword of some sort. */
CLINE;
- yylval.opval = (OP*)newSVOP(OP_CONST, 0, newSVpv(tokenbuf,0));
+ yylval.opval = (OP*)newSVOP(OP_CONST, 0, sv);
yylval.opval->op_private = OPpCONST_BARE;
+ /* And if "Foo::", then that's what it certainly is. */
+
+ if (len)
+ goto safe_bareword;
+
/* See if it's the indirect object for a list operator. */
if (oldoldbufptr &&
(oldoldbufptr == last_lop || oldoldbufptr == last_uni) &&
/* NO SKIPSPACE BEFORE HERE! */
(expect == XREF ||
- (opargs[last_lop_op] >> OASHIFT & 7) == OA_FILEREF) )
+ ((opargs[last_lop_op] >> OASHIFT)& 7) == OA_FILEREF) )
{
bool immediate_paren = *s == '(';
/* (But it's an indir obj regardless for sort.) */
if ((last_lop_op == OP_SORT ||
- (!immediate_paren && (!gv || !GvCV(gv))) ) &&
+ (!immediate_paren && (!gv || !GvCVu(gv))) ) &&
(last_lop_op != OP_MAPSTART && last_lop_op != OP_GREPSTART)){
expect = (last_lop == oldoldbufptr) ? XTERM : XOPERATOR;
goto bareword;
s = skipspace(s);
if (*s == '(') {
CLINE;
+ if (gv && GvCVu(gv)) {
+ for (d = s + 1; *d == ' ' || *d == '\t'; d++) ;
+ if (*d == ')' && (sv = cv_const_sv(GvCV(gv)))) {
+ s = d + 1;
+ goto its_constant;
+ }
+ }
nextval[nexttoke].opval = yylval.opval;
expect = XOPERATOR;
force_next(WORD);
/* If followed by var or block, call it a method (unless sub) */
- if ((*s == '$' || *s == '{') && (!gv || !GvCV(gv))) {
+ if ((*s == '$' || *s == '{') && (!gv || !GvCVu(gv))) {
last_lop = oldbufptr;
last_lop_op = OP_METHOD;
PREBLOCK(METHOD);
/* Not a method, so call it a subroutine (if defined) */
- if (gv && GvCV(gv)) {
- CV* cv = GvCV(gv);
- if (*s == '(') {
- nextval[nexttoke].opval = yylval.opval;
- expect = XTERM;
- force_next(WORD);
- yylval.ival = 0;
- TOKEN('&');
- }
+ if (gv && GvCVu(gv)) {
+ CV* cv;
if (lastchar == '-')
warn("Ambiguous use of -%s resolved as -&%s()",
tokenbuf, tokenbuf);
last_lop = oldbufptr;
last_lop_op = OP_ENTERSUB;
+ /* Check for a constant sub */
+ cv = GvCV(gv);
+ if ((sv = cv_const_sv(cv))) {
+ its_constant:
+ SvREFCNT_dec(((SVOP*)yylval.opval)->op_sv);
+ ((SVOP*)yylval.opval)->op_sv = SvREFCNT_inc(sv);
+ yylval.opval->op_private = 0;
+ TOKEN(WORD);
+ }
+
/* Resolve to GV now. */
op_free(yylval.opval);
yylval.opval = newCVREF(0, newGVOP(OP_GV, 0, gv));
if (hints & HINT_STRICT_SUBS &&
lastchar != '-' &&
strnNE(s,"->",2) &&
+ last_lop_op != OP_TRUNCATE && /* S/F prototype in opcode.pl */
last_lop_op != OP_ACCEPT &&
last_lop_op != OP_PIPE_OP &&
last_lop_op != OP_SOCKPAIR)
warn(warn_reserved, tokenbuf);
}
}
+
+ safe_bareword:
if (lastchar && strchr("*%&", lastchar)) {
warn("Operator or semicolon missing before %c%s",
lastchar, tokenbuf);
TOKEN(WORD);
}
+ case KEY___FILE__:
+ yylval.opval = (OP*)newSVOP(OP_CONST, 0,
+ newSVsv(GvSV(curcop->cop_filegv)));
+ TERM(THING);
+
case KEY___LINE__:
- case KEY___FILE__: {
- if (tokenbuf[2] == 'L')
- (void)sprintf(tokenbuf,"%ld",(long)curcop->cop_line);
- else
- strcpy(tokenbuf, SvPVX(GvSV(curcop->cop_filegv)));
- yylval.opval = (OP*)newSVOP(OP_CONST, 0, newSVpv(tokenbuf,0));
+ yylval.opval = (OP*)newSVOP(OP_CONST, 0,
+ newSVpvf("%ld", (long)curcop->cop_line));
+ TERM(THING);
+
+ case KEY___PACKAGE__:
+ yylval.opval = (OP*)newSVOP(OP_CONST, 0,
+ (curstash
+ ? newSVsv(curstname)
+ : &sv_undef));
TERM(THING);
- }
case KEY___DATA__:
case KEY___END__: {
/*SUPPRESS 560*/
if (rsfp && (!in_eval || tokenbuf[2] == 'D')) {
- char dname[256];
char *pname = "main";
if (tokenbuf[2] == 'D')
pname = HvNAME(curstash ? curstash : defstash);
- sprintf(dname,"%s::DATA", pname);
- gv = gv_fetchpv(dname,TRUE, SVt_PVIO);
+ gv = gv_fetchpv(form("%s::DATA", pname), TRUE, SVt_PVIO);
GvMULTI_on(gv);
if (!GvIO(gv))
GvIOp(gv) = newIO();
IoIFP(GvIOp(gv)) = rsfp;
#if defined(HAS_FCNTL) && defined(F_SETFD)
{
- int fd = fileno(rsfp);
+ int fd = PerlIO_fileno(rsfp);
fcntl(fd,F_SETFD,fd >= 3);
}
#endif
+ /* Mark this internal pseudo-handle as clean */
+ IoFLAGS(GvIOp(gv)) |= IOf_UNTAINT;
if (preprocess)
IoTYPE(GvIOp(gv)) = '|';
- else if ((FILE*)rsfp == stdin)
+ else if ((PerlIO*)rsfp == PerlIO_stdin())
IoTYPE(GvIOp(gv)) = '-';
else
IoTYPE(GvIOp(gv)) = '<';
case KEY_DESTROY:
case KEY_BEGIN:
case KEY_END:
- case KEY_RESTART:
+ case KEY_INIT:
if (expect == XSTATE) {
s = bufptr;
goto really_sub;
if (*s == ':' && s[1] == ':') {
s += 2;
d = s;
- s = scan_word(s, tokenbuf, FALSE, &len);
+ s = scan_word(s, tokenbuf, sizeof tokenbuf, FALSE, &len);
tmp = keyword(tokenbuf, len);
if (tmp < 0)
tmp = -tmp;
case KEY_for:
case KEY_foreach:
yylval.ival = curcop->cop_line;
- while (s < bufend && isSPACE(*s))
- s++;
- if (isIDFIRST(*s))
- croak("Missing $ on loop variable");
+ s = skipspace(s);
+ if (expect == XSTATE && isIDFIRST(*s)) {
+ char *p = s;
+ if ((bufend - p) >= 3 &&
+ strnEQ(p, "my", 2) && isSPACE(*(p + 2)))
+ p += 2;
+ p = skipspace(p);
+ if (isIDFIRST(*p))
+ croak("Missing $ on loop variable");
+ }
OPERATOR(FOR);
case KEY_formline:
FUN0(OP_GPWENT);
case KEY_getpwnam:
- FUN1(OP_GPWNAM);
+ UNI(OP_GPWNAM);
case KEY_getpwuid:
- FUN1(OP_GPWUID);
+ UNI(OP_GPWUID);
case KEY_getpeername:
UNI(OP_GETPEERNAME);
FUN0(OP_GGRENT);
case KEY_getgrnam:
- FUN1(OP_GGRNAM);
+ UNI(OP_GGRNAM);
case KEY_getgrgid:
- FUN1(OP_GGRGID);
+ UNI(OP_GGRGID);
case KEY_getlogin:
FUN0(OP_GETLOGIN);
UNI(OP_LCFIRST);
case KEY_local:
- yylval.ival = 0;
OPERATOR(LOCAL);
case KEY_length:
case KEY_listen:
LOP(OP_LISTEN,XTERM);
+ case KEY_lock:
+ UNI(OP_LOCK);
+
case KEY_lstat:
UNI(OP_LSTAT);
case KEY_my:
in_my = TRUE;
- yylval.ival = 1;
s = skipspace(s);
if (isIDFIRST(*s)) {
- s = scan_word(s, tokenbuf, TRUE, &len);
+ s = scan_word(s, tokenbuf, sizeof tokenbuf, TRUE, &len);
in_my_stash = gv_stashpv(tokenbuf, FALSE);
if (!in_my_stash) {
char tmpbuf[1024];
yyerror(tmpbuf);
}
}
- OPERATOR(LOCAL);
+ OPERATOR(MY);
case KEY_next:
s = force_word(s,WORD,TRUE,FALSE,FALSE);
if (expect != XSTATE)
yyerror("\"no\" not allowed in expression");
s = force_word(s,WORD,FALSE,TRUE,FALSE);
+ s = force_version(s);
yylval.ival = 0;
OPERATOR(USE);
s = scan_str(s);
if (!s)
missingterm((char*)0);
+ if (dowarn && SvLEN(lex_stuff)) {
+ d = SvPV_force(lex_stuff, len);
+ for (; len; --len, ++d) {
+ if (*d == ',') {
+ warn("Possible attempt to separate words with commas");
+ break;
+ }
+ if (*d == '#') {
+ warn("Possible attempt to put comments in qw() list");
+ break;
+ }
+ }
+ }
force_next(')');
nextval[nexttoke].opval = (OP*)newSVOP(OP_CONST, 0, q(lex_stuff));
lex_stuff = Nullsv;
*tokenbuf = '\0';
s = force_word(s,WORD,TRUE,TRUE,FALSE);
if (isIDFIRST(*tokenbuf))
- gv_stashpv(tokenbuf, TRUE);
+ gv_stashpvn(tokenbuf, strlen(tokenbuf), TRUE);
else if (*s == '<')
yyerror("<> should be quotes");
UNI(OP_REQUIRE);
LOP(OP_SETPRIORITY,XTERM);
case KEY_sethostent:
- FUN1(OP_SHOSTENT);
+ UNI(OP_SHOSTENT);
case KEY_setnetent:
- FUN1(OP_SNETENT);
+ UNI(OP_SNETENT);
case KEY_setservent:
- FUN1(OP_SSERVENT);
+ UNI(OP_SSERVENT);
case KEY_setprotoent:
- FUN1(OP_SPROTOENT);
+ UNI(OP_SPROTOENT);
case KEY_setpwent:
FUN0(OP_SPWENT);
if (*s == ';' || *s == ')') /* probably a close */
croak("sort is now a reserved word");
expect = XTERM;
- s = force_word(s,WORD,TRUE,TRUE,TRUE);
+ s = force_word(s,WORD,TRUE,TRUE,FALSE);
LOP(OP_SORT,XREF);
case KEY_split:
s = skipspace(s);
if (isIDFIRST(*s) || *s == '\'' || *s == ':') {
- char tmpbuf[128];
+ char tmpbuf[sizeof tokenbuf];
expect = XBLOCK;
- d = scan_word(s, tmpbuf, TRUE, &len);
+ d = scan_word(s, tmpbuf, sizeof tmpbuf, TRUE, &len);
if (strchr(tmpbuf, ':'))
sv_setpv(subname, tmpbuf);
else {
/* Look for a prototype */
if (*s == '(') {
+ char *p;
+
s = scan_str(s);
if (!s) {
if (lex_stuff)
lex_stuff = Nullsv;
croak("Prototype not terminated");
}
+ /* strip spaces */
+ d = SvPVX(lex_stuff);
+ tmp = 0;
+ for (p = d; *p; ++p) {
+ if (!isSPACE(*p))
+ d[tmp++] = *p;
+ }
+ d[tmp] = '\0';
+ SvCUR(lex_stuff) = tmp;
+
nexttoke++;
nextval[1] = nextval[0];
nexttype[1] = nexttype[0];
case KEY_sysopen:
LOP(OP_SYSOPEN,XTERM);
+ case KEY_sysseek:
+ LOP(OP_SYSSEEK,XTERM);
+
case KEY_sysread:
LOP(OP_SYSREAD,XTERM);
case KEY_use:
if (expect != XSTATE)
yyerror("\"use\" not allowed in expression");
- s = force_word(s,WORD,FALSE,TRUE,FALSE);
+ s = skipspace(s);
+ if(isDIGIT(*s)) {
+ s = force_version(s);
+ if(*s == ';' || (s = skipspace(s), *s == ';')) {
+ nextval[nexttoke].opval = Nullop;
+ force_next(WORD);
+ }
+ }
+ else {
+ s = force_word(s,WORD,FALSE,TRUE,FALSE);
+ s = force_version(s);
+ }
yylval.ival = 1;
OPERATOR(USE);
s = scan_trans(s);
TERM(sublex_start());
}
- }
+ }}
}
I32
-keyword(d, len)
-register char *d;
-I32 len;
+keyword(register char *d, I32 len)
{
switch (*d) {
case '_':
if (d[1] == '_') {
- if (strEQ(d,"__LINE__")) return -KEY___LINE__;
if (strEQ(d,"__FILE__")) return -KEY___FILE__;
+ if (strEQ(d,"__LINE__")) return -KEY___LINE__;
+ if (strEQ(d,"__PACKAGE__")) return -KEY___PACKAGE__;
if (strEQ(d,"__DATA__")) return KEY___DATA__;
if (strEQ(d,"__END__")) return KEY___END__;
}
case 4:
if (strEQ(d,"grep")) return KEY_grep;
if (strEQ(d,"goto")) return KEY_goto;
- if (strEQ(d,"glob")) return -KEY_glob;
+ if (strEQ(d,"glob")) return KEY_glob;
break;
case 6:
if (strEQ(d,"gmtime")) return -KEY_gmtime;
case 'h':
if (strEQ(d,"hex")) return -KEY_hex;
break;
+ case 'I':
+ if (strEQ(d,"INIT")) return KEY_INIT;
+ break;
case 'i':
switch (len) {
case 2:
case 4:
if (strEQ(d,"last")) return KEY_last;
if (strEQ(d,"link")) return -KEY_link;
+ if (strEQ(d,"lock")) return -KEY_lock;
break;
case 5:
if (strEQ(d,"local")) return KEY_local;
}
else if (strEQ(d,"quotemeta")) return -KEY_quotemeta;
break;
- case 'R':
- if (strEQ(d,"RESTART")) return KEY_RESTART;
- break;
case 'r':
switch (len) {
case 3:
if (strEQ(d,"system")) return -KEY_system;
break;
case 7:
- if (strEQ(d,"sysopen")) return -KEY_sysopen;
- if (strEQ(d,"sysread")) return -KEY_sysread;
if (strEQ(d,"symlink")) return -KEY_symlink;
if (strEQ(d,"syscall")) return -KEY_syscall;
+ if (strEQ(d,"sysopen")) return -KEY_sysopen;
+ if (strEQ(d,"sysread")) return -KEY_sysread;
+ if (strEQ(d,"sysseek")) return -KEY_sysseek;
break;
case 8:
if (strEQ(d,"syswrite")) return -KEY_syswrite;
}
static void
-checkcomma(s,name,what)
-register char *s;
-char *name;
-char *what;
+checkcomma(register char *s, char *name, char *what)
{
char *w;
}
if (*w)
for (; *w && isSPACE(*w); w++) ;
- if (!*w || !strchr(";|})]oa!=", *w)) /* an advisory hack only... */
+ if (!*w || !strchr(";|})]oaiuw!=", *w)) /* an advisory hack only... */
warn("%s (...) interpreted as function",name);
}
while (s < bufend && isSPACE(*s))
}
static char *
-scan_word(s, dest, allow_package, slp)
-register char *s;
-char *dest;
-int allow_package;
-STRLEN *slp;
+scan_word(register char *s, char *dest, STRLEN destlen, int allow_package, STRLEN *slp)
{
register char *d = dest;
+ register char *e = d + destlen - 3; /* two-character token, ending NUL */
for (;;) {
+ if (d >= e)
+ croak(ident_too_long);
if (isALNUM(*s))
*d++ = *s++;
else if (*s == '\'' && allow_package && isIDFIRST(s[1])) {
*d++ = ':';
s++;
}
- else if (*s == ':' && s[1] == ':' && allow_package && isIDFIRST(s[2])) {
+ else if (*s == ':' && s[1] == ':' && allow_package && s[2] != '$') {
*d++ = *s++;
*d++ = *s++;
}
}
static char *
-scan_ident(s,send,dest,ck_uni)
-register char *s;
-register char *send;
-char *dest;
-I32 ck_uni;
+scan_ident(register char *s, register char *send, char *dest, STRLEN destlen, I32 ck_uni)
{
register char *d;
+ register char *e;
char *bracket = 0;
char funny = *s++;
if (isSPACE(*s))
s = skipspace(s);
d = dest;
+ e = d + destlen - 3; /* two-character token, ending NUL */
if (isDIGIT(*s)) {
- while (isDIGIT(*s))
+ while (isDIGIT(*s)) {
+ if (d >= e)
+ croak(ident_too_long);
*d++ = *s++;
+ }
}
else {
for (;;) {
+ if (d >= e)
+ croak(ident_too_long);
if (isALNUM(*s))
*d++ = *s++;
else if (*s == '\'' && isIDFIRST(s[1])) {
return s;
}
if (*s == '$' && s[1] &&
- (isALPHA(s[1]) || strchr("$_{", s[1]) || strnEQ(s+1,"::",2)) )
- return s;
+ (isALNUM(s[1]) || strchr("${", s[1]) || strnEQ(s+1,"::",2)) )
+ {
+ if (isDIGIT(s[1]) && lex_state == LEX_INTERPNORMAL)
+ deprecate("\"$$<digit>\" to mean \"${$}<digit>\"");
+ else
+ return s;
+ }
if (*s == '{') {
bracket = s;
s++;
*d = *s++;
d[1] = '\0';
if (*d == '^' && *s && (isUPPER(*s) || strchr("[\\]^_?", *s))) {
- *d = *s++ ^ 64;
+ *d = toCTRL(*s);
+ s++;
}
if (bracket) {
if (isSPACE(s[-1])) {
- while (s < send && (*s == ' ' || *s == '\t')) s++;
- *d = *s;
+ while (s < send) {
+ char ch = *s++;
+ if (ch != ' ' && ch != '\t') {
+ *d = ch;
+ break;
+ }
+ }
}
- if (isALPHA(*d) || *d == '_') {
+ if (isIDFIRST(*d)) {
d++;
while (isALNUM(*s) || *s == ':')
*d++ = *s++;
*d = '\0';
while (s < send && (*s == ' ' || *s == '\t')) s++;
- if ((*s == '[' || *s == '{')) {
+ if ((*s == '[' || (*s == '{' && strNE(dest, "sub")))) {
if (dowarn && keyword(dest, d - dest)) {
char *brack = *s == '[' ? "[...]" : "{...}";
warn("Ambiguous use of %c{%s%s} resolved to %c%s%s",
lex_state = LEX_INTERPEND;
if (funny == '#')
funny = '@';
- if (dowarn &&
+ if (dowarn && lex_state == LEX_NORMAL &&
(keyword(dest, d - dest) || perl_get_cv(dest, FALSE)))
warn("Ambiguous use of %c{%s} resolved to %c%s",
funny, dest, funny, dest);
return s;
}
-void pmflag(pmfl,ch)
-U16* pmfl;
-int ch;
+void pmflag(U16 *pmfl, int ch)
{
- if (ch == 'i') {
- sawi = TRUE;
+ if (ch == 'i')
*pmfl |= PMf_FOLD;
- }
else if (ch == 'g')
*pmfl |= PMf_GLOBAL;
+ else if (ch == 'c')
+ *pmfl |= PMf_CONTINUE;
else if (ch == 'o')
*pmfl |= PMf_KEEP;
else if (ch == 'm')
}
static char *
-scan_pat(start)
-char *start;
+scan_pat(char *start)
{
PMOP *pm;
char *s;
lex_stuff = Nullsv;
croak("Search pattern not terminated");
}
+
pm = (PMOP*)newPMOP(OP_MATCH, 0);
if (multi_open == '?')
pm->op_pmflags |= PMf_ONCE;
-
- while (*s && strchr("iogmsx", *s))
+ while (*s && strchr("iogcmsx", *s))
pmflag(&pm->op_pmflags,*s++);
-
pm->op_pmpermflags = pm->op_pmflags;
+
lex_op = (OP*)pm;
yylval.ival = OP_MATCH;
return s;
}
static char *
-scan_subst(start)
-char *start;
+scan_subst(char *start)
{
register char *s;
register PMOP *pm;
+ I32 first_start;
I32 es = 0;
yylval.ival = OP_NULL;
if (s[-1] == multi_open)
s--;
+ first_start = multi_start;
s = scan_str(s);
if (!s) {
if (lex_stuff)
lex_repl = Nullsv;
croak("Substitution replacement not terminated");
}
+ multi_start = first_start; /* so whole substitution is taken together */
pm = (PMOP*)newPMOP(OP_SUBST, 0);
- while (*s && strchr("iogmsex", *s)) {
+ while (*s && strchr("iogcmsex", *s)) {
if (*s == 'e') {
s++;
es++;
return s;
}
-void
-hoistmust(pm)
-register PMOP *pm;
-{
- if (!pm->op_pmshort && pm->op_pmregexp->regstart &&
- (!pm->op_pmregexp->regmust || pm->op_pmregexp->reganch & ROPT_ANCH)
- ) {
- if (!(pm->op_pmregexp->reganch & ROPT_ANCH))
- pm->op_pmflags |= PMf_SCANFIRST;
- else if (pm->op_pmflags & PMf_FOLD)
- return;
- pm->op_pmshort = SvREFCNT_inc(pm->op_pmregexp->regstart);
- pm->op_pmslen = SvCUR(pm->op_pmshort);
- }
- else if (pm->op_pmregexp->regmust) {/* is there a better short-circuit? */
- if (pm->op_pmshort &&
- sv_eq(pm->op_pmshort,pm->op_pmregexp->regmust))
- {
- if (pm->op_pmflags & PMf_SCANFIRST) {
- SvREFCNT_dec(pm->op_pmshort);
- pm->op_pmshort = Nullsv;
- }
- else {
- SvREFCNT_dec(pm->op_pmregexp->regmust);
- pm->op_pmregexp->regmust = Nullsv;
- return;
- }
- }
- if (!pm->op_pmshort || /* promote the better string */
- ((pm->op_pmflags & PMf_SCANFIRST) &&
- (SvCUR(pm->op_pmshort) < SvCUR(pm->op_pmregexp->regmust)) )){
- SvREFCNT_dec(pm->op_pmshort); /* ok if null */
- pm->op_pmshort = pm->op_pmregexp->regmust;
- pm->op_pmslen = SvCUR(pm->op_pmshort);
- pm->op_pmregexp->regmust = Nullsv;
- pm->op_pmflags |= PMf_SCANFIRST;
- }
- }
-}
-
static char *
-scan_trans(start)
-char *start;
+scan_trans(char *start)
{
register char* s;
- OP *op;
+ OP *o;
short *tbl;
I32 squash;
- I32 delete;
+ I32 Delete;
I32 complement;
yylval.ival = OP_NULL;
if (lex_stuff)
SvREFCNT_dec(lex_stuff);
lex_stuff = Nullsv;
- croak("Translation pattern not terminated");
+ croak("Transliteration pattern not terminated");
}
if (s[-1] == multi_open)
s--;
if (lex_repl)
SvREFCNT_dec(lex_repl);
lex_repl = Nullsv;
- croak("Translation replacement not terminated");
+ croak("Transliteration replacement not terminated");
}
New(803,tbl,256,short);
- op = newPVOP(OP_TRANS, 0, (char*)tbl);
+ o = newPVOP(OP_TRANS, 0, (char*)tbl);
- complement = delete = squash = 0;
+ complement = Delete = squash = 0;
while (*s == 'c' || *s == 'd' || *s == 's') {
if (*s == 'c')
complement = OPpTRANS_COMPLEMENT;
else if (*s == 'd')
- delete = OPpTRANS_DELETE;
+ Delete = OPpTRANS_DELETE;
else
squash = OPpTRANS_SQUASH;
s++;
}
- op->op_private = delete|squash|complement;
+ o->op_private = Delete|squash|complement;
- lex_op = op;
+ lex_op = o;
yylval.ival = OP_TRANS;
return s;
}
static char *
-scan_heredoc(s)
-register char *s;
+scan_heredoc(register char *s)
{
+ dTHR;
SV *herewas;
I32 op_type = OP_SCALAR;
I32 len;
SV *tmpstr;
char term;
register char *d;
+ register char *e;
char *peek;
+ int outer = (rsfp && !(lex_inwhat == OP_SCALAR));
s += 2;
d = tokenbuf;
- if (!rsfp)
+ e = tokenbuf + sizeof tokenbuf - 1;
+ if (!outer)
*d++ = '\n';
for (peek = s; *peek == ' ' || *peek == '\t'; peek++) ;
if (*peek && strchr("`'\"",*peek)) {
s = peek;
term = *s++;
- s = cpytill(d,s,bufend,term,&len);
+ s = delimcpy(d, e, s, bufend, term, &len);
+ d += len;
if (s < bufend)
s++;
- d += len;
}
else {
if (*s == '\\')
term = '"';
if (!isALNUM(*s))
deprecate("bare << to mean <<\"\"");
- while (isALNUM(*s))
- *d++ = *s++;
- } /* assuming tokenbuf won't clobber */
+ for (; isALNUM(*s); s++) {
+ if (d < e)
+ *d++ = *s;
+ }
+ }
+ if (d >= tokenbuf + sizeof tokenbuf - 1)
+ croak("Delimiter for here document is too long");
*d++ = '\n';
*d = '\0';
len = d - tokenbuf;
d = "\n";
- if (rsfp || !(d=ninstr(s,bufend,d,d+1)))
+ if (outer || !(d=ninstr(s,bufend,d,d+1)))
herewas = newSVpv(s,bufend-s);
else
s--, herewas = newSVpv(s,d-s);
multi_start = curcop->cop_line;
multi_open = multi_close = '<';
term = *tokenbuf;
- if (!rsfp) {
+ if (!outer) {
d = s;
while (s < bufend &&
- (*s != term || bcmp(s,tokenbuf,len) != 0) ) {
+ (*s != term || memNE(s,tokenbuf,len)) ) {
if (*s++ == '\n')
curcop->cop_line++;
}
}
sv_setpvn(tmpstr,d+1,s-d);
s += len - 1;
+ curcop->cop_line++; /* the preceding stmt passes a newline */
+
sv_catpvn(herewas,s,bufend-s);
sv_setsv(linestr,herewas);
- oldoldbufptr = oldbufptr = bufptr = s = SvPVX(linestr);
+ oldoldbufptr = oldbufptr = bufptr = s = linestart = SvPVX(linestr);
bufend = SvPVX(linestr) + SvCUR(linestr);
}
else
sv_setpvn(tmpstr,"",0); /* avoid "uninitialized" warning */
while (s >= bufend) { /* multiple line string? */
- if (!rsfp ||
- !(oldoldbufptr = oldbufptr = s = filter_gets(linestr, rsfp))) {
+ if (!outer ||
+ !(oldoldbufptr = oldbufptr = s = linestart = filter_gets(linestr, rsfp, 0))) {
curcop->cop_line = multi_start;
missingterm(tokenbuf);
}
curcop->cop_line++;
- if (perldb && curstash != debstash) {
+ if (PERLDB_LINE && curstash != debstash) {
SV *sv = NEWSV(88,0);
sv_upgrade(sv, SVt_PVMG);
(I32)curcop->cop_line,sv);
}
bufend = SvPVX(linestr) + SvCUR(linestr);
- if (*s == term && bcmp(s,tokenbuf,len) == 0) {
+ if (*s == term && memEQ(s,tokenbuf,len)) {
s = bufend - 1;
*s = ' ';
sv_catsv(linestr,herewas);
return s;
}
+/* scan_inputsymbol
+ takes: current position in input buffer
+ returns: new position in input buffer
+ side-effects: yylval and lex_op are set.
+
+ This code handles:
+
+ <> read from ARGV
+ <FH> read from filehandle
+ <pkg::FH> read from package qualified filehandle
+ <pkg'FH> read from package qualified filehandle
+ <$fh> read from filehandle in $fh
+ <*.h> filename glob
+
+*/
+
static char *
-scan_inputsymbol(start)
-char *start;
+scan_inputsymbol(char *start)
{
- register char *s = start;
+ register char *s = start; /* current position in buffer */
register char *d;
+ register char *e;
I32 len;
- d = tokenbuf;
- s = cpytill(d, s+1, bufend, '>', &len);
- if (s < bufend)
- s++;
- else
+ d = tokenbuf; /* start of temp holding space */
+ e = tokenbuf + sizeof tokenbuf; /* end of temp holding space */
+ s = delimcpy(d, e, s + 1, bufend, '>', &len); /* extract until > */
+
+ /* die if we didn't have space for the contents of the <>,
+ or if it didn't end
+ */
+
+ if (len >= sizeof tokenbuf)
+ croak("Excessively long <> operator");
+ if (s >= bufend)
croak("Unterminated <> operator");
+ s++;
+
+ /* check for <$fh>
+ Remember, only scalar variables are interpreted as filehandles by
+ this code. Anything more complex (e.g., <$fh{$num}>) will be
+ treated as a glob() call.
+ This code makes use of the fact that except for the $ at the front,
+ a scalar variable and a filehandle look the same.
+ */
if (*d == '$' && d[1]) d++;
+
+ /* allow <Pkg'VALUE> or <Pkg::VALUE> */
while (*d && (isALNUM(*d) || *d == '\'' || *d == ':'))
d++;
+
+ /* If we've tried to read what we allow filehandles to look like, and
+ there's still text left, then it must be a glob() and not a getline.
+ Use scan_str to pull out the stuff between the <> and treat it
+ as nothing more than a string.
+ */
+
if (d - tokenbuf != len) {
yylval.ival = OP_GLOB;
set_csh();
s = scan_str(start);
if (!s)
- croak("Glob not terminated");
+ croak("Glob not terminated");
return s;
}
else {
+ /* we're in a filehandle read situation */
d = tokenbuf;
+
+ /* turn <> into <ARGV> */
if (!len)
(void)strcpy(d,"ARGV");
+
+ /* if <$fh>, create the ops to turn the variable into a
+ filehandle
+ */
if (*d == '$') {
I32 tmp;
- if (tmp = pad_findmy(d)) {
- OP *op = newOP(OP_PADSV, 0);
- op->op_targ = tmp;
- lex_op = (OP*)newUNOP(OP_READLINE, 0, newUNOP(OP_RV2GV, 0, op));
+
+ /* try to find it in the pad for this block, otherwise find
+ add symbol table ops
+ */
+ if ((tmp = pad_findmy(d)) != NOT_IN_PAD) {
+ OP *o = newOP(OP_PADSV, 0);
+ o->op_targ = tmp;
+ lex_op = (OP*)newUNOP(OP_READLINE, 0, newUNOP(OP_RV2GV, 0, o));
}
else {
GV *gv = gv_fetchpv(d+1,TRUE, SVt_PV);
newUNOP(OP_RV2SV, 0,
newGVOP(OP_GV, 0, gv))));
}
+ /* we created the ops in lex_op, so make yylval.ival a null op */
yylval.ival = OP_NULL;
}
+
+ /* If it's none of the above, it must be a literal filehandle
+ (<Foo::BAR> or <FOO>) so build a simple readline OP */
else {
GV *gv = gv_fetchpv(d,TRUE, SVt_PVIO);
lex_op = (OP*)newUNOP(OP_READLINE, 0, newGVOP(OP_GV, 0, gv));
yylval.ival = OP_NULL;
}
}
+
return s;
}
+
+/* scan_str
+ takes: start position in buffer
+ returns: position to continue reading from buffer
+ side-effects: multi_start, multi_close, lex_repl or lex_stuff, and
+ updates the read buffer.
+
+ This subroutine pulls a string out of the input. It is called for:
+ q single quotes q(literal text)
+ ' single quotes 'literal text'
+ qq double quotes qq(interpolate $here please)
+ " double quotes "interpolate $here please"
+ qx backticks qx(/bin/ls -l)
+ ` backticks `/bin/ls -l`
+ qw quote words @EXPORT_OK = qw( func() $spam )
+ m// regexp match m/this/
+ s/// regexp substitute s/this/that/
+ tr/// string transliterate tr/this/that/
+ y/// string transliterate y/this/that/
+ ($*@) sub prototypes sub foo ($)
+ <> readline or globs <FOO>, <>, <$fh>, or <*.c>
+
+ In most of these cases (all but <>, patterns and transliterate)
+ yylex() calls scan_str(). m// makes yylex() call scan_pat() which
+ calls scan_str(). s/// makes yylex() call scan_subst() which calls
+ scan_str(). tr/// and y/// make yylex() call scan_trans() which
+ calls scan_str().
+
+ It skips whitespace before the string starts, and treats the first
+ character as the delimiter. If the delimiter is one of ([{< then
+ the corresponding "close" character )]}> is used as the closing
+ delimiter. It allows quoting of delimiters, and if the string has
+ balanced delimiters ([{<>}]) it allows nesting.
+
+ The lexer always reads these strings into lex_stuff, except in the
+ case of the operators which take *two* arguments (s/// and tr///)
+ when it checks to see if lex_stuff is full (presumably with the 1st
+ arg to s or tr) and if so puts the string into lex_repl.
+
+*/
+
static char *
-scan_str(start)
-char *start;
+scan_str(char *start)
{
- SV *sv;
- char *tmps;
- register char *s = start;
- register char term;
- register char *to;
- I32 brackets = 1;
-
+ dTHR;
+ SV *sv; /* scalar value: string */
+ char *tmps; /* temp string, used for delimiter matching */
+ register char *s = start; /* current position in the buffer */
+ register char term; /* terminating character */
+ register char *to; /* current position in the sv's data */
+ I32 brackets = 1; /* bracket nesting level */
+
+ /* skip space before the delimiter */
if (isSPACE(*s))
s = skipspace(s);
+
+ /* mark where we are, in case we need to report errors */
CLINE;
+
+ /* after skipping whitespace, the next character is the terminator */
term = *s;
+ /* mark where we are */
multi_start = curcop->cop_line;
multi_open = term;
+
+ /* find corresponding closing delimiter */
if (term && (tmps = strchr("([{< )]}> )]}>",term)))
term = tmps[5];
multi_close = term;
+ /* create a new SV to hold the contents. 87 is leak category, I'm
+ assuming. 80 is the SV's initial length. What a random number. */
sv = NEWSV(87,80);
sv_upgrade(sv, SVt_PVIV);
SvIVX(sv) = term;
(void)SvPOK_only(sv); /* validate pointer */
+
+ /* move past delimiter and try to read a complete string */
s++;
for (;;) {
+ /* extend sv if need be */
SvGROW(sv, SvCUR(sv) + (bufend - s) + 1);
+ /* set 'to' to the next character in the sv's string */
to = SvPVX(sv)+SvCUR(sv);
+
+ /* if open delimiter is the close delimiter read unbridle */
if (multi_open == multi_close) {
for (; s < bufend; s++,to++) {
+ /* embedded newlines increment the current line number */
if (*s == '\n' && !rsfp)
curcop->cop_line++;
+ /* handle quoted delimiters */
if (*s == '\\' && s+1 < bufend && term != '\\') {
if (s[1] == term)
s++;
+ /* any other quotes are simply copied straight through */
else
*to++ = *s++;
}
+ /* terminate when run out of buffer (the for() condition), or
+ have found the terminator */
else if (*s == term)
break;
*to = *s;
}
}
+
+ /* if the terminator isn't the same as the start character (e.g.,
+ matched brackets), we have to allow more in the quoting, and
+ be prepared for nested brackets.
+ */
else {
+ /* read until we run out of string, or we find the terminator */
for (; s < bufend; s++,to++) {
+ /* embedded newlines increment the line count */
if (*s == '\n' && !rsfp)
curcop->cop_line++;
- if (*s == '\\' && s+1 < bufend && term != '\\') {
- if (s[1] == term)
+ /* backslashes can escape the open or closing characters */
+ if (*s == '\\' && s+1 < bufend) {
+ if ((s[1] == multi_open) || (s[1] == multi_close))
s++;
else
*to++ = *s++;
}
- else if (*s == term && --brackets <= 0)
+ /* allow nested opens and closes */
+ else if (*s == multi_close && --brackets <= 0)
break;
else if (*s == multi_open)
brackets++;
*to = *s;
}
}
+ /* terminate the copied string and update the sv's end-of-string */
*to = '\0';
SvCUR_set(sv, to - SvPVX(sv));
- if (s < bufend) break; /* string ends on this line? */
+ /*
+ * this next chunk reads more into the buffer if we're not done yet
+ */
+ if (s < bufend) break; /* handle case where we are done yet :-) */
+
+ /* if we're out of file, or a read fails, bail and reset the current
+ line marker so we can report where the unterminated string began
+ */
if (!rsfp ||
- !(oldoldbufptr = oldbufptr = s = filter_gets(linestr, rsfp))) {
+ !(oldoldbufptr = oldbufptr = s = linestart = filter_gets(linestr, rsfp, 0))) {
sv_free(sv);
curcop->cop_line = multi_start;
return Nullch;
}
+ /* we read a line, so increment our line counter */
curcop->cop_line++;
- if (perldb && curstash != debstash) {
+
+ /* update debugger info */
+ if (PERLDB_LINE && curstash != debstash) {
SV *sv = NEWSV(88,0);
sv_upgrade(sv, SVt_PVMG);
av_store(GvAV(curcop->cop_filegv),
(I32)curcop->cop_line, sv);
}
+
+ /* having changed the buffer, we must update bufend */
bufend = SvPVX(linestr) + SvCUR(linestr);
}
+
+ /* at this point, we have successfully read the delimited string */
+
multi_end = curcop->cop_line;
s++;
+
+ /* if we allocated too much space, give some back */
if (SvCUR(sv) + 5 < SvLEN(sv)) {
SvLEN_set(sv, SvCUR(sv) + 1);
Renew(SvPVX(sv), SvLEN(sv), char);
}
+
+ /* decide whether this is the first or second quoted string we've read
+ for this op
+ */
+
if (lex_stuff)
lex_repl = sv;
else
return s;
}
+/*
+ scan_num
+ takes: pointer to position in buffer
+ returns: pointer to new position in buffer
+ side-effects: builds ops for the constant in yylval.op
+
+ Read a number in any of the formats that Perl accepts:
+
+ 0(x[0-7A-F]+)|([0-7]+)
+ [\d_]+(\.[\d_]*)?[Ee](\d+)
+
+ Underbars (_) are allowed in decimal numbers. If -w is on,
+ underbars before a decimal point must be at three digit intervals.
+
+ Like most scan_ routines, it uses the tokenbuf buffer to hold the
+ thing it reads.
+
+ If it reads a number without a decimal point or an exponent, it will
+ try converting the number to an integer and see if it can do so
+ without loss of precision.
+*/
+
char *
-scan_num(start)
-char *start;
+scan_num(char *start)
{
- register char *s = start;
- register char *d;
- I32 tryi32;
- double value;
- SV *sv;
- I32 floatit;
- char *lastub = 0;
+ register char *s = start; /* current position in buffer */
+ register char *d; /* destination in temp buffer */
+ register char *e; /* end of temp buffer */
+ I32 tryiv; /* used to see if it can be an int */
+ double value; /* number read, as a double */
+ SV *sv; /* place to put the converted number */
+ I32 floatit; /* boolean: int or float? */
+ char *lastub = 0; /* position of last underbar */
+ static char number_too_long[] = "Number too long";
+
+ /* We use the first character to decide what type of number this is */
switch (*s) {
default:
- croak("panic: scan_num");
+ croak("panic: scan_num");
+
+ /* if it starts with a 0, it could be an octal number, a decimal in
+ 0.13 disguise, or a hexadecimal number.
+ */
case '0':
{
- U32 i;
+ /* variables:
+ u holds the "number so far"
+ shift the power of 2 of the base (hex == 4, octal == 3)
+ overflowed was the number more than we can hold?
+
+ Shift is used when we add a digit. It also serves as an "are
+ we in octal or hex?" indicator to disallow hex characters when
+ in octal mode.
+ */
+ UV u;
I32 shift;
+ bool overflowed = FALSE;
+ /* check for hex */
if (s[1] == 'x') {
shift = 4;
s += 2;
}
+ /* check for a decimal in disguise */
else if (s[1] == '.')
goto decimal;
+ /* so it must be octal */
else
shift = 3;
- i = 0;
+ u = 0;
+
+ /* read the rest of the octal number */
for (;;) {
+ UV n, b; /* n is used in the overflow test, b is the digit we're adding on */
+
switch (*s) {
+
+ /* if we don't mention it, we're done */
default:
goto out;
+
+ /* _ are ignored */
case '_':
s++;
break;
+
+ /* 8 and 9 are not octal */
case '8': case '9':
if (shift != 4)
yyerror("Illegal octal digit");
/* FALL THROUGH */
+
+ /* octal digits */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7':
- i <<= shift;
- i += *s++ & 15;
- break;
+ b = *s++ & 15; /* ASCII digit -> value of digit */
+ goto digit;
+
+ /* hex digits */
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ /* make sure they said 0x */
if (shift != 4)
goto out;
- i <<= 4;
- i += (*s++ & 7) + 9;
+ b = (*s++ & 7) + 9;
+
+ /* Prepare to put the digit we have onto the end
+ of the number so far. We check for overflows.
+ */
+
+ digit:
+ n = u << shift; /* make room for the digit */
+ if (!overflowed && (n >> shift) != u) {
+ warn("Integer overflow in %s number",
+ (shift == 4) ? "hex" : "octal");
+ overflowed = TRUE;
+ }
+ u = n | b; /* add the digit to the end */
break;
}
}
+
+ /* if we get here, we had success: make a scalar value from
+ the number.
+ */
out:
sv = NEWSV(92,0);
- tryi32 = i;
- if (tryi32 == i && tryi32 >= 0)
- sv_setiv(sv,tryi32);
- else
- sv_setnv(sv,(double)i);
+ sv_setuv(sv, u);
}
break;
+
+ /*
+ handle decimal numbers.
+ we're also sent here when we read a 0 as the first digit
+ */
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9': case '.':
decimal:
d = tokenbuf;
+ e = tokenbuf + sizeof tokenbuf - 6; /* room for various punctuation */
floatit = FALSE;
+
+ /* read next group of digits and _ and copy into d */
while (isDIGIT(*s) || *s == '_') {
+ /* skip underscores, checking for misplaced ones
+ if -w is on
+ */
if (*s == '_') {
if (dowarn && lastub && s - lastub != 3)
warn("Misplaced _ in number");
lastub = ++s;
}
- else
+ else {
+ /* check for end of fixed-length buffer */
+ if (d >= e)
+ croak(number_too_long);
+ /* if we're ok, copy the character */
*d++ = *s++;
+ }
}
+
+ /* final misplaced underbar check */
if (dowarn && lastub && s - lastub != 3)
warn("Misplaced _ in number");
+
+ /* read a decimal portion if there is one. avoid
+ 3..5 being interpreted as the number 3. followed
+ by .5
+ */
if (*s == '.' && s[1] != '.') {
floatit = TRUE;
*d++ = *s++;
- while (isDIGIT(*s) || *s == '_') {
- if (*s == '_')
- s++;
- else
- *d++ = *s++;
+
+ /* copy, ignoring underbars, until we run out of
+ digits. Note: no misplaced underbar checks!
+ */
+ for (; isDIGIT(*s) || *s == '_'; s++) {
+ /* fixed length buffer check */
+ if (d >= e)
+ croak(number_too_long);
+ if (*s != '_')
+ *d++ = *s;
}
}
+
+ /* read exponent part, if present */
if (*s && strchr("eE",*s) && strchr("+-0123456789",s[1])) {
floatit = TRUE;
s++;
+
+ /* regardless of whether user said 3E5 or 3e5, use lower 'e' */
*d++ = 'e'; /* At least some Mach atof()s don't grok 'E' */
+
+ /* allow positive or negative exponent */
if (*s == '+' || *s == '-')
*d++ = *s++;
- while (isDIGIT(*s))
+
+ /* read digits of exponent (no underbars :-) */
+ while (isDIGIT(*s)) {
+ if (d >= e)
+ croak(number_too_long);
*d++ = *s++;
+ }
}
+
+ /* terminate the string */
*d = '\0';
+
+ /* make an sv from the string */
sv = NEWSV(92,0);
+ /* reset numeric locale in case we were earlier left in Swaziland */
+ SET_NUMERIC_STANDARD();
value = atof(tokenbuf);
- tryi32 = I_32(value);
- if (!floatit && (double)tryi32 == value)
- sv_setiv(sv,tryi32);
+
+ /*
+ See if we can make do with an integer value without loss of
+ precision. We use I_V to cast to an int, because some
+ compilers have issues. Then we try casting it back and see
+ if it was the same. We only do this if we know we
+ specifically read an integer.
+
+ Note: if floatit is true, then we don't need to do the
+ conversion at all.
+ */
+ tryiv = I_V(value);
+ if (!floatit && (double)tryiv == value)
+ sv_setiv(sv, tryiv);
else
- sv_setnv(sv,value);
+ sv_setnv(sv, value);
break;
}
+ /* make the op for the constant and return */
+
yylval.opval = newSVOP(OP_CONST, 0, sv);
return s;
}
static char *
-scan_formline(s)
-register char *s;
+scan_formline(register char *s)
{
+ dTHR;
register char *eol;
register char *t;
SV *stuff = newSVpv("",0);
}
s = eol;
if (rsfp) {
- s = filter_gets(linestr, rsfp);
- oldoldbufptr = oldbufptr = bufptr = SvPVX(linestr);
+ s = filter_gets(linestr, rsfp, 0);
+ oldoldbufptr = oldbufptr = bufptr = linestart = SvPVX(linestr);
bufend = bufptr + SvCUR(linestr);
if (!s) {
s = bufptr;
}
static void
-set_csh()
+set_csh(void)
{
#ifdef CSH
if (!cshlen)
#endif
}
-int
-start_subparse()
+I32
+start_subparse(I32 is_format, U32 flags)
{
- int oldsavestack_ix = savestack_ix;
+ dTHR;
+ I32 oldsavestack_ix = savestack_ix;
CV* outsidecv = compcv;
AV* comppadlist;
}
save_I32(&subline);
save_item(subname);
- SAVEINT(padix);
+ SAVEI32(padix);
SAVESPTR(curpad);
SAVESPTR(comppad);
SAVESPTR(comppad_name);
SAVESPTR(compcv);
- SAVEINT(comppad_name_fill);
- SAVEINT(min_intro_pending);
- SAVEINT(max_intro_pending);
- SAVEINT(pad_reset_pending);
+ SAVEI32(comppad_name_fill);
+ SAVEI32(min_intro_pending);
+ SAVEI32(max_intro_pending);
+ SAVEI32(pad_reset_pending);
compcv = (CV*)NEWSV(1104,0);
- sv_upgrade((SV *)compcv, SVt_PVCV);
+ sv_upgrade((SV *)compcv, is_format ? SVt_PVFM : SVt_PVCV);
+ CvFLAGS(compcv) |= flags;
comppad = newAV();
+ av_push(comppad, Nullsv);
+ curpad = AvARRAY(comppad);
comppad_name = newAV();
comppad_name_fill = 0;
min_intro_pending = 0;
- av_push(comppad, Nullsv);
- curpad = AvARRAY(comppad);
padix = 0;
subline = curcop->cop_line;
+#ifdef USE_THREADS
+ av_store(comppad_name, 0, newSVpv("@_", 2));
+ curpad[0] = (SV*)newAV();
+ SvPADMY_on(curpad[0]); /* XXX Needed? */
+ CvOWNER(compcv) = 0;
+ New(666, CvMUTEXP(compcv), 1, perl_mutex);
+ MUTEX_INIT(CvMUTEXP(compcv));
+#endif /* USE_THREADS */
comppadlist = newAV();
AvREAL_off(comppadlist);
av_store(comppadlist, 1, (SV*)comppad);
CvPADLIST(compcv) = comppadlist;
- CvOUTSIDE(compcv) = (CV*)SvREFCNT_inc((SV*)outsidecv);
+ CvOUTSIDE(compcv) = (CV*)SvREFCNT_inc(outsidecv);
+#ifdef USE_THREADS
+ CvOWNER(compcv) = 0;
+ New(666, CvMUTEXP(compcv), 1, perl_mutex);
+ MUTEX_INIT(CvMUTEXP(compcv));
+#endif /* USE_THREADS */
return oldsavestack_ix;
}
int
-yywarn(s)
-char *s;
+yywarn(char *s)
{
+ dTHR;
--error_count;
in_eval |= 2;
yyerror(s);
}
int
-yyerror(s)
-char *s;
+yyerror(char *s)
{
- char tmpbuf[258];
- char *tname = tmpbuf;
-
- if (bufptr > oldoldbufptr && bufptr - oldoldbufptr < 200 &&
+ dTHR;
+ char *where = NULL;
+ char *context = NULL;
+ int contlen = -1;
+ SV *msg;
+
+ if (!yychar || (yychar == ';' && !rsfp))
+ where = "at EOF";
+ else if (bufptr > oldoldbufptr && bufptr - oldoldbufptr < 200 &&
oldoldbufptr != oldbufptr && oldbufptr != bufptr) {
while (isSPACE(*oldoldbufptr))
oldoldbufptr++;
- sprintf(tname,"near \"%.*s\"",bufptr - oldoldbufptr, oldoldbufptr);
+ context = oldoldbufptr;
+ contlen = bufptr - oldoldbufptr;
}
else if (bufptr > oldbufptr && bufptr - oldbufptr < 200 &&
oldbufptr != bufptr) {
while (isSPACE(*oldbufptr))
oldbufptr++;
- sprintf(tname,"near \"%.*s\"",bufptr - oldbufptr, oldbufptr);
+ context = oldbufptr;
+ contlen = bufptr - oldbufptr;
}
else if (yychar > 255)
- tname = "next token ???";
- else if (!yychar || (yychar == ';' && !rsfp))
- (void)strcpy(tname,"at EOF");
+ where = "next token ???";
else if ((yychar & 127) == 127) {
if (lex_state == LEX_NORMAL ||
(lex_state == LEX_KNOWNEXT && lex_defer == LEX_NORMAL))
- (void)strcpy(tname,"at end of line");
+ where = "at end of line";
else if (lex_inpat)
- (void)strcpy(tname,"within pattern");
+ where = "within pattern";
else
- (void)strcpy(tname,"within string");
+ where = "within string";
}
- else if (yychar < 32)
- (void)sprintf(tname,"next char ^%c",yychar+64);
+ else {
+ SV *where_sv = sv_2mortal(newSVpv("next char ", 0));
+ if (yychar < 32)
+ sv_catpvf(where_sv, "^%c", toCTRL(yychar));
+ else if (isPRINT_LC(yychar))
+ sv_catpvf(where_sv, "%c", yychar);
+ else
+ sv_catpvf(where_sv, "\\%03o", yychar & 255);
+ where = SvPVX(where_sv);
+ }
+ msg = sv_2mortal(newSVpv(s, 0));
+ sv_catpvf(msg, " at %_ line %ld, ",
+ GvSV(curcop->cop_filegv), (long)curcop->cop_line);
+ if (context)
+ sv_catpvf(msg, "near \"%.*s\"\n", contlen, context);
else
- (void)sprintf(tname,"next char %c",yychar);
- (void)sprintf(buf, "%s at %s line %d, %s\n",
- s,SvPVX(GvSV(curcop->cop_filegv)),curcop->cop_line,tname);
- if (curcop->cop_line == multi_end && multi_start < multi_end) {
- sprintf(buf+strlen(buf),
- " (Might be a runaway multi-line %c%c string starting on line %ld)\n",
- multi_open,multi_close,(long)multi_start);
+ sv_catpvf(msg, "%s\n", where);
+ if (multi_start < multi_end && (U32)(curcop->cop_line - multi_end) <= 1) {
+ sv_catpvf(msg,
+ " (Might be a runaway multi-line %c%c string starting on line %ld)\n",
+ (int)multi_open,(int)multi_close,(long)multi_start);
multi_end = 0;
}
if (in_eval & 2)
- warn("%s",buf);
+ warn("%_", msg);
else if (in_eval)
- sv_catpv(GvSV(errgv),buf);
+ sv_catsv(ERRSV, msg);
else
- fputs(buf,stderr);
+ PerlIO_write(PerlIO_stderr(), SvPVX(msg), SvCUR(msg));
if (++error_count >= 10)
- croak("%s has too many errors.\n",
- SvPVX(GvSV(curcop->cop_filegv)));
+ croak("%_ has too many errors.\n", GvSV(curcop->cop_filegv));
in_my = 0;
in_my_stash = Nullhv;
return 0;
}
+
+