#include "EXTERN.h" #include "perl.h" #include "callparser1.h" #include "XSUB.h" /* stolen (with modifications) from Scope::Escape::Sugar */ #define SVt_PADNAME SVt_PVMG #ifndef COP_SEQ_RANGE_LOW_set # define COP_SEQ_RANGE_LOW_set(sv,val) \ do { ((XPVNV*)SvANY(sv))->xnv_u.xpad_cop_seq.xlow = val; } while(0) # define COP_SEQ_RANGE_HIGH_set(sv,val) \ do { ((XPVNV*)SvANY(sv))->xnv_u.xpad_cop_seq.xhigh = val; } while(0) #endif /* !COP_SEQ_RANGE_LOW_set */ /* * pad handling * * The public API for the pad system is lacking any way to add items to * the pad. This is a minimal implementation of the necessary facilities. * It doesn't warn about shadowing. */ #define pad_add_my_pvn(namepv, namelen, type) \ THX_pad_add_my_pvn(aTHX_ namepv, namelen, type) static PADOFFSET THX_pad_add_my_pvn(pTHX_ char const *namepv, STRLEN namelen, svtype type) { PADOFFSET offset; SV *namesv, *myvar; myvar = *av_fetch(PL_comppad, AvFILLp(PL_comppad) + 1, 1); offset = AvFILLp(PL_comppad); SvPADMY_on(myvar); SvUPGRADE(myvar, type); PL_curpad = AvARRAY(PL_comppad); namesv = newSV_type(SVt_PADNAME); sv_setpvn(namesv, namepv, namelen); COP_SEQ_RANGE_LOW_set(namesv, PL_cop_seqmax); COP_SEQ_RANGE_HIGH_set(namesv, PERL_PADSEQ_INTRO); PL_cop_seqmax++; av_store(PL_comppad_name, offset, namesv); return offset; } #define pad_add_my_sv(namesv, type) THX_pad_add_my_sv(aTHX_ namesv, type) static PADOFFSET THX_pad_add_my_sv(pTHX_ SV *namesv, svtype type) { char const *pv; STRLEN len; pv = SvPV(namesv, len); return pad_add_my_pvn(pv, len, type); } #define pad_add_my_scalar_sv(namesv) THX_pad_add_my_sv(aTHX_ namesv, SVt_NULL) #define pad_add_my_array_sv(namesv) THX_pad_add_my_sv(aTHX_ namesv, SVt_PVAV) #define pad_add_my_hash_sv(namesv) THX_pad_add_my_sv(aTHX_ namesv, SVt_PVHV) #define pad_add_my_scalar_pvn(namepv, namelen) \ THX_pad_add_my_pvn(aTHX_ namepv, namelen, SVt_NULL) #define pad_add_my_array_pvn(namepv, namelen) \ THX_pad_add_my_pvn(aTHX_ namepv, namelen, SVt_PVAV) #define pad_add_my_hash_pvn(namepv, namelen) \ THX_pad_add_my_pvn(aTHX_ namepv, namelen, SVt_PVHV) /* * parser pieces * * These functions reimplement fairly low-level parts of the Perl syntax, * using the character-level public lexer API. */ #define DEMAND_IMMEDIATE 0x00000001 #define DEMAND_NOCONSUME 0x00000002 #define demand_unichar(c, f) THX_demand_unichar(aTHX_ c, f) static void THX_demand_unichar(pTHX_ I32 c, U32 flags) { if(!(flags & DEMAND_IMMEDIATE)) lex_read_space(0); if(lex_peek_unichar(0) != c) croak("syntax error"); if(!(flags & DEMAND_NOCONSUME)) lex_read_unichar(0); } #define parse_idword(prefix) THX_parse_idword(aTHX_ prefix) static SV *THX_parse_idword(pTHX_ char const *prefix) { STRLEN prefixlen, idlen; SV *sv; char *start, *s, c; s = start = PL_parser->bufptr; c = *s; if(!isIDFIRST(c)) croak("syntax error"); do { c = *++s; } while(isALNUM(c)); lex_read_to(s); prefixlen = strlen(prefix); idlen = s-start; sv = sv_2mortal(newSV(prefixlen + idlen)); Copy(prefix, SvPVX(sv), prefixlen, char); Copy(start, SvPVX(sv)+prefixlen, idlen, char); SvPVX(sv)[prefixlen + idlen] = 0; SvCUR_set(sv, prefixlen + idlen); SvPOK_on(sv); return sv; } #define parse_varname(sigil) THX_parse_varname(aTHX_ sigil) static SV *THX_parse_varname(pTHX_ const char *sigil) { demand_unichar(sigil[0], DEMAND_IMMEDIATE); lex_read_space(0); return parse_idword(sigil); } #define parse_scalar_varname() THX_parse_varname(aTHX_ "$") #define parse_array_varname() THX_parse_varname(aTHX_ "@") #define parse_hash_varname() THX_parse_varname(aTHX_ "%") /* end stolen from Scope::Escape::Sugar */ #define parse_parameter_default(i, padoffset) THX_parse_parameter_default(aTHX_ i, padoffset) static OP *THX_parse_parameter_default(pTHX_ IV i, PADOFFSET padoffset) { SV *name; OP *default_expr, *check_args, *get_var, *assign_default; char sigil; lex_read_space(0); default_expr = parse_arithexpr(0); check_args = newBINOP(OP_LE, 0, newUNOP(OP_RV2AV, 0, newGVOP(OP_GV, 0, gv_fetchpv("_", 0, SVt_PVAV))), newSVOP(OP_CONST, 0, newSViv(i))); name = newSVsv(*av_fetch(PL_comppad_name, padoffset, 0)); sigil = SvPVX(name)[0]; if (sigil == '$') { get_var = newOP(OP_PADSV, 0); } else if (sigil == '@') { get_var = newOP(OP_PADAV, 0); } else if (sigil == '%') { get_var = newOP(OP_PADHV, 0); } else { croak("weird pad entry %"SVf, name); } get_var->op_targ = padoffset; assign_default = newASSIGNOP(OPf_STACKED, get_var, 0, default_expr); return newLOGOP(OP_AND, 0, check_args, assign_default); } #define parse_function_prototype() THX_parse_function_prototype(aTHX) static OP *THX_parse_function_prototype(pTHX) { OP *myvars, *defaults, *get_args, *arg_assign; IV i = 0; SV *seen_slurpy = NULL; demand_unichar('(', DEMAND_IMMEDIATE); lex_read_space(0); if (lex_peek_unichar(0) == ')') { lex_read_unichar(0); return NULL; } myvars = newLISTOP(OP_LIST, 0, NULL, NULL); defaults = newLISTOP(OP_LINESEQ, 0, NULL, NULL); for (;;) { OP *pad_op; char next; I32 type; SV *name; lex_read_space(0); next = lex_peek_unichar(0); if (next == '$') { name = parse_scalar_varname(); if (seen_slurpy) { croak("Can't declare parameter %"SVf" after slurpy parameter %"SVf, name, seen_slurpy); } pad_op = newOP(OP_PADSV, 0); pad_op->op_targ = pad_add_my_scalar_sv(name); } else if (next == '@') { name = parse_array_varname(); if (seen_slurpy) { croak("Can't declare parameter %"SVf" after slurpy parameter %"SVf, name, seen_slurpy); } pad_op = newOP(OP_PADAV, 0); pad_op->op_targ = pad_add_my_array_sv(name); seen_slurpy = name; } else if (next == '%') { name = parse_hash_varname(); if (seen_slurpy) { croak("Can't declare parameter %"SVf" after slurpy parameter %"SVf, name, seen_slurpy); } pad_op = newOP(OP_PADHV, 0); pad_op->op_targ = pad_add_my_hash_sv(name); seen_slurpy = name; } else { croak("syntax error"); } op_append_elem(OP_LIST, myvars, pad_op); lex_read_space(0); next = lex_peek_unichar(0); if (next == '=') { OP *set_default; lex_read_unichar(0); set_default = parse_parameter_default(i, pad_op->op_targ); op_append_elem(OP_LINESEQ, defaults, newSTATEOP(0, NULL, set_default)); lex_read_space(0); next = lex_peek_unichar(0); } i++; if (next == ',') { lex_read_unichar(0); } else if (next == ')') { lex_read_unichar(0); break; } else { croak("syntax error"); } } myvars = Perl_localize(aTHX_ myvars, 1); myvars = Perl_sawparens(aTHX_ myvars); get_args = newUNOP(OP_RV2AV, 0, newGVOP(OP_GV, 0, gv_fetchpv("_", 0, SVt_PVAV))); arg_assign = newASSIGNOP(OPf_STACKED, myvars, 0, get_args); return op_prepend_elem(OP_LINESEQ, newSTATEOP(0, NULL, arg_assign), defaults); } static OP *parse_fun(pTHX_ GV *namegv, SV *psobj, U32 *flagsp) { I32 floor; SV *function_name = NULL; CV *code; OP *arg_assign = NULL, *block, *name; lex_read_space(0); if (isIDFIRST(*(PL_parser->bufptr)) || *(PL_parser->bufptr) == ':') { floor = start_subparse(0, 0); function_name = sv_2mortal(newSVpvs("")); while (isIDFIRST(*(PL_parser->bufptr)) || *(PL_parser->bufptr) == ':') { if (lex_peek_unichar(0) == ':') { demand_unichar(':', DEMAND_IMMEDIATE); demand_unichar(':', DEMAND_IMMEDIATE); sv_catpvs(function_name, "::"); } else { sv_catsv(function_name, parse_idword("")); } } } else { floor = start_subparse(0, CVf_ANON); } lex_read_space(0); if (lex_peek_unichar(0) == '(') { arg_assign = parse_function_prototype(); } demand_unichar('{', DEMAND_NOCONSUME); block = parse_block(0); if (arg_assign) { block = op_prepend_elem(OP_LINESEQ, newSTATEOP(0, NULL, arg_assign), block); } if (function_name) { SV *code; *flagsp |= CALLPARSER_STATEMENT; SvREFCNT_inc(function_name); name = newSVOP(OP_CONST, 0, function_name); code = newRV_inc((SV*)newATTRSUB(floor, name, NULL, NULL, block)); return newOP(OP_NULL, 0); } else { OP *code; code = newANONSUB(floor, NULL, block); return newLISTOP(OP_LIST, 0, code, NULL); } } static OP *check_fun(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { OP *kids, *args; kids = cUNOPx(entersubop)->op_first; args = cLISTOPx(kids)->op_first->op_sibling; if (args->op_type == OP_NULL) { op_free(entersubop); return newOP(OP_NULL, 0); } else { return entersubop; } } MODULE = Fun PACKAGE = Fun PROTOTYPES: DISABLE BOOT: { cv_set_call_parser(get_cv("Fun::fun", 0), parse_fun, &PL_sv_undef); cv_set_call_checker(get_cv("Fun::fun", 0), check_fun, &PL_sv_undef); }