From 30a65edd7abe56021b5222d2e73960005f3e9647 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Sun, 25 Jun 2023 17:30:42 +0200 Subject: [PATCH] Berry Walrus operator ':=' (#18963) --- CHANGELOG.md | 1 + lib/libesp32/berry/src/be_code.c | 7 +++-- lib/libesp32/berry/src/be_code.h | 2 +- lib/libesp32/berry/src/be_debug.c | 2 ++ lib/libesp32/berry/src/be_lexer.c | 4 ++- lib/libesp32/berry/src/be_lexer.h | 4 ++- lib/libesp32/berry/src/be_module.c | 2 +- lib/libesp32/berry/src/be_parser.c | 46 +++++++++++++++++++++--------- 8 files changed, 48 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1334a481b..04908ab9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. - Berry add global function `format` as a simpler syntax to `string.format` - Berry added f-strings as an alternative to string formatting - Matter display the remote Device Name instead of IP address +- Berry Walrus operator ':=' ### Breaking Changed diff --git a/lib/libesp32/berry/src/be_code.c b/lib/libesp32/berry/src/be_code.c index 1a890390e..4c2d99940 100644 --- a/lib/libesp32/berry/src/be_code.c +++ b/lib/libesp32/berry/src/be_code.c @@ -670,14 +670,15 @@ static void setsfxvar(bfuncinfo *finfo, bopcode op, bexpdesc *e1, int src) /* Assign expr e2 to e1 */ /* e1 must be in a register and have a valid idx */ +/* if `keep_reg` is true, do not release registre */ /* return 1 if assignment was possible, 0 if type is not compatible */ -int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2) +int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, bbool keep_reg) { int src = exp2reg(finfo, e2, e1->type == ETLOCAL ? e1->v.idx : -1); /* Convert e2 to kreg */ /* If e1 is a local variable, use the register */ - if (e1->type != ETLOCAL || e1->v.idx != src) { + if (!keep_reg && (e1->type != ETLOCAL || e1->v.idx != src)) { free_expreg(finfo, e2); /* free source (checks only ETREG) */ /* TODO e2 is at top */ } switch (e1->type) { @@ -887,7 +888,7 @@ void be_code_import(bfuncinfo *finfo, bexpdesc *m, bexpdesc *v) codeABC(finfo, OP_IMPORT, dst, src, 0); m->type = ETREG; m->v.idx = dst; - be_code_setvar(finfo, v, m); + be_code_setvar(finfo, v, m, bfalse); } } diff --git a/lib/libesp32/berry/src/be_code.h b/lib/libesp32/berry/src/be_code.h index cc45d4e06..f0ef2e194 100644 --- a/lib/libesp32/berry/src/be_code.h +++ b/lib/libesp32/berry/src/be_code.h @@ -16,7 +16,7 @@ int be_code_allocregs(bfuncinfo *finfo, int count); void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e); void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2, int dst); int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e); -int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2); +int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, bbool keep_reg); int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e); int be_code_jump(bfuncinfo *finfo); void be_code_jumpto(bfuncinfo *finfo, int dst); diff --git a/lib/libesp32/berry/src/be_debug.c b/lib/libesp32/berry/src/be_debug.c index 39a4cd61c..142c4ea5e 100644 --- a/lib/libesp32/berry/src/be_debug.c +++ b/lib/libesp32/berry/src/be_debug.c @@ -266,7 +266,9 @@ static void hook_callnative(bvm *vm, int mask) be_stack_require(vm, BE_STACK_FREE_MIN + 2); info.type = mask; info.line = cf->lineinfo->linenumber; +#if BE_DEBUG_SOURCE_FILE info.source = str(cl->proto->source); +#endif info.func_name = str(cl->proto->name); info.data = hb->data; hb->hook(vm, &info); diff --git a/lib/libesp32/berry/src/be_lexer.c b/lib/libesp32/berry/src/be_lexer.c index 6c2a3e7f2..394a84007 100644 --- a/lib/libesp32/berry/src/be_lexer.c +++ b/lib/libesp32/berry/src/be_lexer.c @@ -752,7 +752,9 @@ static btokentype lexer_next(blexer *lexer) case '}': next(lexer); return OptRBR; case ',': next(lexer); return OptComma; case ';': next(lexer); return OptSemic; - case ':': next(lexer); return OptColon; + case ':': + next(lexer); + return check_next(lexer, '=') ? OptWalrus : OptColon; case '?': next(lexer); return OptQuestion; case '^': return scan_assign(lexer, OptXorAssign, OptBitXor); case '~': next(lexer); return OptFlip; diff --git a/lib/libesp32/berry/src/be_lexer.h b/lib/libesp32/berry/src/be_lexer.h index 3780c79f7..e166a1a64 100644 --- a/lib/libesp32/berry/src/be_lexer.h +++ b/lib/libesp32/berry/src/be_lexer.h @@ -89,7 +89,9 @@ typedef enum { KeyTry, /* keyword try */ KeyExcept, /* keyword except */ KeyRaise, /* keyword raise */ - KeyStatic /* keyword static */ + KeyStatic, /* keyword static */ + /* Walrus operator */ + OptWalrus, /* operator, := */ } btokentype; struct blexerreader { diff --git a/lib/libesp32/berry/src/be_module.c b/lib/libesp32/berry/src/be_module.c index 99b9bbc08..17e5c4dcd 100644 --- a/lib/libesp32/berry/src/be_module.c +++ b/lib/libesp32/berry/src/be_module.c @@ -126,9 +126,9 @@ static char* fixpath(bvm *vm, bstring *path, size_t *size) { char *buffer; const char *split, *base; +#if BE_DEBUG_SOURCE_FILE bvalue *func = vm->cf->func; bclosure *cl = var_toobj(func); -#if BE_DEBUG_SOURCE_FILE if (var_isclosure(func)) { base = str(cl->proto->source); /* get the source file path */ } else { diff --git a/lib/libesp32/berry/src/be_parser.c b/lib/libesp32/berry/src/be_parser.c index 59e57d067..df319318f 100644 --- a/lib/libesp32/berry/src/be_parser.c +++ b/lib/libesp32/berry/src/be_parser.c @@ -80,6 +80,7 @@ typedef struct { static void stmtlist(bparser *parser); static void block(bparser *parser, int type); static void expr(bparser *parser, bexpdesc *e); +static void walrus_expr(bparser *parser, bexpdesc *e); static void sub_expr(bparser *parser, bexpdesc *e, int prio); static const bbyte binary_op_prio_tab[] = { @@ -224,17 +225,17 @@ static void end_block(bparser *parser) end_block_ex(parser, -1); } +#if BE_DEBUG_SOURCE_FILE /* Return the name of the source for this parser, could be `string`, `stdin` or the name of the current function */ static bstring* parser_source(bparser *parser) { -#if BE_DEBUG_SOURCE_FILE if (parser->finfo) { return parser->finfo->proto->source; } -#endif return be_newstr(parser->vm, parser->lexer.fname); } +#endif /* Initialize a function block and create a new `bprotoˋ */ static void begin_func(bparser *parser, bfuncinfo *finfo, bblockinfo *binfo) @@ -368,7 +369,7 @@ static btokentype get_unary_op(bparser *parser) static btokentype get_assign_op(bparser *parser) { btokentype op = next_type(parser); - if (op >= OptAssign && op <= OptRsfAssign) { + if ((op >= OptAssign && op <= OptRsfAssign) || op == OptWalrus) { return op; } return OP_NOT_ASSIGN; @@ -631,7 +632,7 @@ static bproto* funcbody(bparser *parser, bstring *name, bclass *c, int type) new_var(parser, parser_newstr(parser, "_class"), &e1); /* new implicit variable '_class' */ init_exp(&e2, ETCONST, 0); be_code_implicit_class(parser->finfo, &e2, c); - be_code_setvar(parser->finfo, &e1, &e2); + be_code_setvar(parser->finfo, &e1, &e2, bfalse); finfo.proto->varg |= BE_VA_STATICMETHOD; } stmtlist(parser); /* parse statement without final `end` */ @@ -735,7 +736,7 @@ static void map_nextmember(bparser *parser, bexpdesc *l) match_token(parser, OptColon); /* ':' */ expr(parser, &e); /* value in `e` */ check_var(parser, &e); /* check if value is correct */ - be_code_setvar(finfo, &v, &e); /* set suffi INDEX value to e */ + be_code_setvar(finfo, &v, &e, bfalse); /* set suffi INDEX value to e */ } static void list_expr(bparser *parser, bexpdesc *e) @@ -892,8 +893,7 @@ static void primary_expr(bparser *parser, bexpdesc *e) switch (next_type(parser)) { case OptLBK: /* '(' expr ')' */ scan_next_token(parser); /* skip '(' */ - /* sub_expr() is more efficient because there is no need to initialize e. */ - sub_expr(parser, e, ASSIGN_OP_PRIO); + expr(parser, e); check_var(parser, e); match_token(parser, OptRBK); /* skip ')' */ break; @@ -1040,7 +1040,7 @@ static void assign_expr(bparser *parser) if (check_newvar(parser, &e)) { /* new variable */ new_var(parser, e.v.s, &e); } - if (be_code_setvar(parser->finfo, &e, &e1)) { + if (be_code_setvar(parser->finfo, &e, &e1, bfalse)) { parser->lexer.linenumber = line; parser_error(parser, "try to assign constant expressions."); @@ -1125,13 +1125,33 @@ static void sub_expr(bparser *parser, bexpdesc *e, int prio) } } +static void walrus_expr(bparser *parser, bexpdesc *e) +{ + int line = parser->lexer.linenumber; + sub_expr(parser, e, ASSIGN_OP_PRIO); /* left expression */ + btokentype op = next_type(parser); + if (op == OptWalrus) { + bexpdesc e1 = *e; /* copy var to e1, e will get the result of expression */ + scan_next_token(parser); /* skip ':=' */ + expr(parser, e); + if (check_newvar(parser, &e1)) { /* new variable */ + new_var(parser, e1.v.s, e); + } + if (be_code_setvar(parser->finfo, &e1, e, btrue /* do not release register */ )) { + parser->lexer.linenumber = line; + parser_error(parser, + "try to assign constant expressions."); + } + } +} + /* Parse new expression and return value in `e` (overwritten) */ /* Initializes an empty expdes and parse subexpr */ /* Always allocates a new temp register at top of freereg */ static void expr(bparser *parser, bexpdesc *e) { init_exp(e, ETVOID, 0); - sub_expr(parser, e, ASSIGN_OP_PRIO); + walrus_expr(parser, e); } static void expr_stmt(bparser *parser) @@ -1250,7 +1270,7 @@ static void for_iter(bparser *parser, bstring *var, bexpdesc *it) finfo->binfo->beginpc = finfo->pc; /* itvar = .it() */ init_exp(&e, ETLOCAL, new_localvar(parser, var)); /* new itvar */ - be_code_setvar(finfo, &e, it); /* code function to variable '.it' */ + be_code_setvar(finfo, &e, it, bfalse); /* code function to variable '.it' */ be_code_call(finfo, e.v.idx, 0); /* itvar <- call .it() */ stmtlist(parser); } @@ -1444,7 +1464,7 @@ static void class_static_assignment_expr(bparser *parser, bexpdesc *e, bstring * key.v.s = name; be_code_member(parser->finfo, &e1, &key); /* compute member accessor */ - be_code_setvar(parser->finfo, &e1, &e2); /* set member */ + be_code_setvar(parser->finfo, &e1, &e2, bfalse); /* set member */ } } @@ -1568,7 +1588,7 @@ static void classstaticclass_stmt(bparser *parser, bclass *c_out, bexpdesc *e_ou key.v.s = name; /* assign the class to the static member */ be_code_member(parser->finfo, &e1, &key); /* compute member accessor */ - be_code_setvar(parser->finfo, &e1, &e_class); /* set member */ + be_code_setvar(parser->finfo, &e1, &e_class, bfalse); /* set member */ } else { parser_error(parser, "class name error"); } @@ -1620,7 +1640,7 @@ static void var_field(bparser *parser) init_exp(&e2, ETNIL, 0); } new_var(parser, name, &e1); /* new variable */ - be_code_setvar(parser->finfo, &e1, &e2); + be_code_setvar(parser->finfo, &e1, &e2, bfalse); } static void var_stmt(bparser *parser)