diff options
author | Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> | 2022-12-24 13:17:47 +0000 |
---|---|---|
committer | Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> | 2023-02-10 12:07:02 +0000 |
commit | 99b497aa90ed7db99d29a301b47c91fba65c9cb3 (patch) | |
tree | 09e231f3a6838cdb2df9db9454981b9205046ba1 /toke.c | |
parent | b40895ae558e0aff0c347785dafeaaff40a01801 (diff) | |
download | perl-99b497aa90ed7db99d29a301b47c91fba65c9cb3.tar.gz |
Initial attack at basic 'class' feature
Adds a new experimental warning, feature, keywords and enough parsing to
implement basic classes with an empty `new` constructor method.
Inject a $self lexical into method bodies; populate it with the object instance, suitably shifted
Creates a new OP_METHSTART opcode to perform method setup
Define an aux flag to remark which stashes are classes
Basic implementation of fields.
Basic anonymous methods.
Diffstat (limited to 'toke.c')
-rw-r--r-- | toke.c | 65 |
1 files changed, 58 insertions, 7 deletions
@@ -235,6 +235,7 @@ static const char* const lex_state_names[] = { #define PRETERMBLOCK(retval) return (PL_expect = XTERMBLOCK,PL_bufptr = s, REPORT(retval)) #define PREREF(retval) return (PL_expect = XREF,PL_bufptr = s, REPORT(retval)) #define TERM(retval) return (CLINE, PL_expect = XOPERATOR, PL_bufptr = s, REPORT(retval)) +#define PHASERBLOCK(f) return (pl_yylval.ival=f, PL_expect = XBLOCK, PL_bufptr = s, REPORT((int)PHASER)) #define POSTDEREF(f) return (PL_bufptr = s, S_postderef(aTHX_ REPORT(f),s[1])) #define LOOPX(f) return (PL_bufptr = force_word(s,BAREWORD,TRUE,FALSE), \ pl_yylval.ival=f, \ @@ -441,16 +442,20 @@ static struct debug_tokens { DEBUG_TOKEN (OPNUM, FUNC1), DEBUG_TOKEN (NONE, HASHBRACK), DEBUG_TOKEN (IVAL, KW_CATCH), + DEBUG_TOKEN (IVAL, KW_CLASS), DEBUG_TOKEN (IVAL, KW_CONTINUE), DEBUG_TOKEN (IVAL, KW_DEFAULT), DEBUG_TOKEN (IVAL, KW_DO), DEBUG_TOKEN (IVAL, KW_ELSE), DEBUG_TOKEN (IVAL, KW_ELSIF), + DEBUG_TOKEN (IVAL, KW_FIELD), DEBUG_TOKEN (IVAL, KW_GIVEN), DEBUG_TOKEN (IVAL, KW_FOR), DEBUG_TOKEN (IVAL, KW_FORMAT), DEBUG_TOKEN (IVAL, KW_IF), DEBUG_TOKEN (IVAL, KW_LOCAL), + DEBUG_TOKEN (IVAL, KW_METHOD_anon), + DEBUG_TOKEN (IVAL, KW_METHOD_named), DEBUG_TOKEN (IVAL, KW_MY), DEBUG_TOKEN (IVAL, KW_PACKAGE), DEBUG_TOKEN (IVAL, KW_REQUIRE), @@ -5382,7 +5387,10 @@ yyl_sub(pTHX_ char *s, const int key) bool have_name, have_proto; STRLEN len; SV *format_name = NULL; - bool is_sigsub = FEATURE_SIGNATURES_IS_ENABLED; + bool is_method = (key == KEY_method); + + /* method always implies signatures */ + bool is_sigsub = is_method || FEATURE_SIGNATURES_IS_ENABLED; SSize_t off = s-SvPVX(PL_linestr); char *d; @@ -5461,9 +5469,9 @@ yyl_sub(pTHX_ char *s, const int key) if ( !(*s == ':' && s[1] != ':') && (*s != '{' && *s != '(') && key != KEY_format) { - assert(key == KEY_sub || key == KEY_AUTOLOAD || - key == KEY_DESTROY || key == KEY_BEGIN || - key == KEY_UNITCHECK || key == KEY_CHECK || + assert(key == KEY_sub || key == KEY_method || + key == KEY_AUTOLOAD || key == KEY_DESTROY || + key == KEY_BEGIN || key == KEY_UNITCHECK || key == KEY_CHECK || key == KEY_INIT || key == KEY_END || key == KEY_my || key == KEY_state || key == KEY_our); @@ -5479,18 +5487,23 @@ yyl_sub(pTHX_ char *s, const int key) PL_lex_stuff = NULL; force_next(THING); } + if (!have_name) { if (PL_curstash) sv_setpvs(PL_subname, "__ANON__"); else sv_setpvs(PL_subname, "__ANON__::__ANON__"); - if (is_sigsub) + if (is_method) + TOKEN(KW_METHOD_anon); + else if (is_sigsub) TOKEN(KW_SUB_anon_sig); else TOKEN(KW_SUB_anon); } force_ident_maybe_lex('&'); - if (is_sigsub) + if (is_method) + TOKEN(KW_METHOD_named); + else if (is_sigsub) TOKEN(KW_SUB_named_sig); else TOKEN(KW_SUB_named); @@ -7815,6 +7828,16 @@ yyl_word_or_keyword(pTHX_ char *s, STRLEN len, I32 key, I32 orig_keyword, struct return yyl_sub(aTHX_ PL_bufptr, key); return yyl_just_a_word(aTHX_ s, len, orig_keyword, c); + case KEY_ADJUST: + Perl_ck_warner_d(aTHX_ + packWARN(WARN_EXPERIMENTAL__CLASS), "ADJUST is experimental"); + + /* The way that KEY_CHECK et.al. are handled currently are nothing + * short of crazy. We won't copy that model for new phasers, but use + * this as an experiment to test if this will work + */ + PHASERBLOCK(KEY_ADJUST); + case KEY_abs: UNI(OP_ABS); @@ -7852,6 +7875,15 @@ yyl_word_or_keyword(pTHX_ char *s, STRLEN len, I32 key, I32 orig_keyword, struct case KEY_chop: UNI(OP_CHOP); + case KEY_class: + Perl_ck_warner_d(aTHX_ + packWARN(WARN_EXPERIMENTAL__CLASS), "class is experimental"); + + s = force_word(s,BAREWORD,FALSE,TRUE); + s = skipspace(s); + s = force_strict_version(s); + PREBLOCK(KW_CLASS); + case KEY_continue: /* We have to disambiguate the two senses of "continue". If the next token is a '{' then @@ -8005,6 +8037,18 @@ yyl_word_or_keyword(pTHX_ char *s, STRLEN len, I32 key, I32 orig_keyword, struct case KEY_endgrent: FUN0(OP_EGRENT); + case KEY_field: + /* TODO: maybe this should use the same parser/grammar structures as + * `my`, but it's also rather messy because of the `our` conflation + */ + Perl_ck_warner_d(aTHX_ + packWARN(WARN_EXPERIMENTAL__CLASS), "field is experimental"); + + croak_kw_unless_class("field"); + + PL_parser->in_my = KEY_field; + OPERATOR(KW_FIELD); + case KEY_finally: Perl_ck_warner_d(aTHX_ packWARN(WARN_EXPERIMENTAL__TRY), "try/catch/finally is experimental"); @@ -8537,6 +8581,12 @@ yyl_word_or_keyword(pTHX_ char *s, STRLEN len, I32 key, I32 orig_keyword, struct case KEY_substr: LOP(OP_SUBSTR,XTERM); + case KEY_method: + /* For now we just treat 'method' identical to 'sub' plus a warning */ + Perl_ck_warner_d(aTHX_ + packWARN(WARN_EXPERIMENTAL__CLASS), "method is experimental"); + return yyl_sub(aTHX_ s, KEY_method); + case KEY_format: case KEY_sub: return yyl_sub(aTHX_ s, key); @@ -9816,7 +9866,8 @@ S_pending_ident(pTHX) /* PL_no_myglob is constant */ GCC_DIAG_IGNORE_STMT(-Wformat-nonliteral); yyerror_pv(Perl_form(aTHX_ PL_no_myglob, - PL_in_my == KEY_my ? "my" : "state", + PL_in_my == KEY_my ? "my" : + PL_in_my == KEY_field ? "field" : "state", *PL_tokenbuf == '&' ? "subroutine" : "variable", PL_tokenbuf), UTF ? SVf_UTF8 : 0); |