8000 py: Implement PEP 526, syntax for variable annotations by dpgeorge · Pull Request #6126 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

py: Implement PEP 526, syntax for variable annotations #6126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions py/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1981,7 +1981,8 @@ STATIC void compile_async_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
#endif

STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
if (MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
mp_parse_node_t pn_rhs = pns->nodes[1];
if (MP_PARSE_NODE_IS_NULL(pn_rhs)) {
if (comp->is_repl && comp->scope_cur->kind == SCOPE_MODULE) {
// for REPL, evaluate then print the expression
compile_load_id(comp, MP_QSTR___repl_print__);
Expand All @@ -1999,10 +2000,26 @@ STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
EMIT(pop_top); // discard last result since this is a statement and leaves nothing on the stack
}
}
} else if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pns->nodes[1];
} else if (MP_PARSE_NODE_IS_STRUCT(pn_rhs)) {
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pn_rhs;
int kind = MP_PARSE_NODE_STRUCT_KIND(pns1);
if (kind == PN_expr_stmt_augassign) {
if (kind == PN_annassign) {
// the annotation is in pns1->nodes[0] and is ignored
if (MP_PARSE_NODE_IS_NULL(pns1->nodes[1])) {
// an annotation of the form "x: y"
// inside a function this declares "x" as a local
if (comp->scope_cur->kind == SCOPE_FUNCTION) {
if (MP_PARSE_NODE_IS_ID(pns->nodes[0])) {
qstr lhs = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
scope_find_or_add_id(comp->scope_cur, lhs, ID_INFO_KIND_LOCAL);
}
}
} else {
// an assigned annotation of the form "x: y = z"
pn_rhs = pns1->nodes[1];
goto plain_assign;
}
} else if (kind == PN_expr_stmt_augassign) {
c_assign(comp, pns->nodes[0], ASSIGN_AUG_LOAD); // lhs load for aug assign
compile_node(comp, pns1->nodes[1]); // rhs
assert(MP_PARSE_NODE_IS_TOKEN(pns1->nodes[0]));
Expand All @@ -2027,10 +2044,10 @@ STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
} else {
plain_assign:
#if MICROPY_COMP_DOUBLE_TUPLE_ASSIGN
if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_testlist_star_expr)
if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_rhs, PN_testlist_star_expr)
&& MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)) {
mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t *)pns->nodes[0];
pns1 = (mp_parse_node_struct_t *)pns->nodes[1];
pns1 = (mp_parse_node_struct_t *)pn_rhs;
uint32_t n_pns0 = MP_PARSE_NODE_STRUCT_NUM_NODES(pns0);
// Can only optimise a tuple-to-tuple assignment when all of the following hold:
// - equal number of items in LHS and RHS tuples
Expand Down Expand Up @@ -2070,7 +2087,7 @@ STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
}
#endif

compile_node(comp, pns->nodes[1]); // rhs
compile_node(comp, pn_rhs); // rhs
c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store
}
} else {
Expand Down Expand Up @@ -2375,7 +2392,7 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar
dblstar_args_node = pns_arg;
} else if (MP_PARSE_NODE_STRUCT_KIND(pns_arg) == PN_argument) {
#if MICROPY_PY_ASSIGN_EXPR
if (MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_argument_4)) {
if (MP_PARSE_NODE_IS_STRUCT_KIND(pns_arg->nodes[1], PN_argument_3)) {
compile_namedexpr_helper(comp, pns_arg->nodes[0], ((mp_parse_node_struct_t *)pns_arg->nodes[1])->nodes[0]);
n_positional++;
} else
Expand Down
34 changes: 18 additions & 16 deletions py/grammar.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
// - zero_or_more is implemented using opt_rule around a one_or_more rule
// - don't put opt_rule in arguments of or rule; instead, wrap the call to this or rule in opt_rule

// Generic sub-rules used by multiple rules below.

DEF_RULE_NC(generic_colon_test, and_ident(2), tok(DEL_COLON), rule(test))
DEF_RULE_NC(generic_equal_test, and_ident(2), tok(DEL_EQUAL), rule(test))

// # Start symbols for the grammar:
// # single_input is a single interactive statement;
// # file_input is a module or sequence of commands read from an input file;
Expand Down Expand Up @@ -71,19 +76,16 @@ DEF_RULE_NC(funcdefrettype, and_ident(2), tok(DEL_MINUS_MORE), rule(test))
// note: typedargslist lets through more than is allowed, compiler does further checks
DEF_RULE_NC(typedargslist, list_with_end, rule(typedargslist_item), tok(DEL_COMMA))
DEF_RULE_NC(typedargslist_item, or(3), rule(typedargslist_name), rule(typedargslist_star), rule(typedargslist_dbl_star))
DEF_RULE_NC(typedargslist_name, and_ident(3), tok(NAME), opt_rule(typedargslist_colon), opt_rule(typedargslist_equal))
DEF_RULE_NC(typedargslist_name, and_ident(3), tok(NAME), opt_rule(generic_colon_test), opt_rule(generic_equal_test))
DEF_RULE_NC(typedargslist_star, and(2), tok(OP_STAR), opt_rule(tfpdef))
DEF_RULE_NC(typedargslist_dbl_star, and(3), tok(OP_DBL_STAR), tok(NAME), opt_rule(typedargslist_colon))
DEF_RULE_NC(typedargslist_colon, and_ident(2), tok(DEL_COLON), rule(test))
DEF_RULE_NC(typedargslist_equal, and_ident(2), tok(DEL_EQUAL), rule(test))
DEF_RULE_NC(tfpdef, and(2), tok(NAME), opt_rule(typedargslist_colon))
DEF_RULE_NC(typedargslist_dbl_star, and(3), tok(OP_DBL_STAR), tok(NAME), opt_rule(generic_colon_test))
DEF_RULE_NC(tfpdef, and(2), tok(NAME), opt_rule(generic_colon_test))
// note: varargslist lets through more than is allowed, compiler does further checks
DEF_RULE_NC(varargslist, list_with_end, rule(varargslist_item), tok(DEL_COMMA))
DEF_RULE_NC(varargslist_item, or(3), rule(varargslist_name), rule(varargslist_star), rule(varargslist_dbl_star))
DEF_RULE_NC(varargslist_name, and_ident(2), tok(NAME), opt_rule(varargslist_equal))
DEF_RULE_NC(varargslist_name, and_ident(2), tok(NAME), opt_rule(generic_equal_test))
DEF_RULE_NC(varargslist_star, and(2), tok(OP_STAR), opt_rule(vfpdef))
DEF_RULE_NC(varargslist_dbl_star, and(2), tok(OP_DBL_STAR), tok(NAME))
DEF_RULE_NC(varargslist_equal, and_ident(2), tok(DEL_EQUAL), rule(test))
DEF_RULE_NC(vfpdef, and_ident(1), tok(NAME))

// stmt: compound_stmt | simple_stmt
Expand All @@ -96,20 +98,22 @@ DEF_RULE_NC(simple_stmt, and_ident(2), rule(simple_stmt_2), tok(NEWLINE))
DEF_RULE(simple_stmt_2, c(generic_all_nodes), list_with_end, rule(small_stmt), tok(DEL_SEMICOLON))

// small_stmt: expr_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt
// expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) | ('=' (yield_expr|testlist_star_expr))*)
// expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | ('=' (yield_expr|testlist_star_expr))*)
// testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
// annassign: ':' test ['=' (yield_expr|testlist_star_expr)]
// augassign: '+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//='
// # For normal assignments, additional restrictions enforced by the interpreter
// # For normal and annotated assignments, additional restrictions enforced by the interpreter

DEF_RULE_NC(small_stmt, or(8), rule(del_stmt), rule(pass_stmt), rule(flow_stmt), rule(import_stmt), rule(global_stmt), rule(nonlocal_stmt), rule(assert_stmt), rule(expr_stmt))
DEF_RULE(expr_stmt, c(expr_stmt), and(2), rule(testlist_star_expr), opt_rule(expr_stmt_2))
DEF_RULE_NC(expr_stmt_2, or(2), rule(expr_stmt_augassign), rule(expr_stmt_assign_list))
DEF_RULE_NC(expr_stmt_2, or(3), rule(annassign), rule(expr_stmt_augassign), rule(expr_stmt_assign_list))
DEF_RULE_NC(expr_stmt_augassign, and_ident(2), rule(augassign), rule(expr_stmt_6))
DEF_RULE_NC(expr_stmt_assign_list, one_or_more, rule(expr_stmt_assign))
DEF_RULE_NC(expr_stmt_assign, and_ident(2), tok(DEL_EQUAL), rule(expr_stmt_6))
DEF_RULE_NC(expr_stmt_6, or(2), rule(yield_expr), rule(testlist_star_expr))
DEF_RULE(testlist_star_expr, c(generic_tuple), list_with_end, rule(testlist_star_expr_2), tok(DEL_COMMA))
DEF_RULE_NC(testlist_star_expr_2, or(2), rule(star_expr), rule(test))
DEF_RULE_NC(annassign, and(3), tok(DEL_COLON), rule(test), opt_rule(expr_stmt_assign))
DEF_RULE_NC(augassign, or(13), tok(DEL_PLUS_EQUAL), tok(DEL_MINUS_EQUAL), tok(DEL_STAR_EQUAL), tok(DEL_AT_EQUAL), tok(DEL_SLASH_EQUAL), tok(DEL_PERCENT_EQUAL), tok(DEL_AMPERSAND_EQUAL), tok(DEL_PIPE_EQUAL), tok(DEL_CARET_EQUAL), tok(DEL_DBL_LESS_EQUAL), tok(DEL_DBL_MORE_EQUAL), tok(DEL_DBL_STAR_EQUAL), tok(DEL_DBL_SLASH_EQUAL))

// del_stmt: 'del' exprlist
Expand Down Expand Up @@ -318,8 +322,7 @@ DEF_RULE(testlist, c(generic_tuple), list_with_end, rule(test), tok(DEL_COMMA))
// TODO dictorsetmaker lets through more than is allowed
DEF_RULE_NC(dictorsetmaker, and_ident(2), rule(dictorsetmaker_item), opt_rule(dictorsetmaker_tail))
#if MICROPY_PY_BUILTINS_SET
DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and_ident(2), rule(test), opt_rule(dictorsetmaker_colon))
DEF_RULE_NC(dictorsetmaker_colon, and_ident(2), tok(DEL_COLON), rule(test))
DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and_ident(2), rule(test), opt_rule(generic_colon_test))
#else
DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and(3), rule(test), tok(DEL_COLON), rule(test))
#endif
Expand Down Expand Up @@ -349,12 +352,11 @@ DEF_RULE_NC(arglist_dbl_star, and(2), tok(OP_DBL_STAR), rule(test))

DEF_RULE_NC(argument, and_ident(2), rul E29B e(test), opt_rule(argument_2))
#if MICROPY_PY_ASSIGN_EXPR
DEF_RULE_NC(argument_2, or(3), rule(comp_for), rule(argument_3), rule(argument_4))
DEF_RULE_NC(argument_4, and(2), tok(OP_ASSIGN), rule(test))
DEF_RULE_NC(argument_2, or(3), rule(comp_for), rule(generic_equal_test), rule(argument_3))
DEF_RULE_NC(argument_3, and(2), tok(OP_ASSIGN), rule(test))
#else
DEF_RULE_NC(argument_2, or(2), rule(comp_for), rule(argument_3))
DEF_RULE_NC(argument_2, or(2), rule(comp_for), rule(generic_equal_test))
#endif
DEF_RULE_NC(argument_3, and_ident(2), tok(DEL_EQUAL), rule(test))
DEF_RULE_NC(comp_iter, or(2), rule(comp_for), rule(comp_if))
DEF_RULE_NC(comp_for, and_blank(5), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(or_test), opt_rule(comp_iter))
DEF_RULE_NC(comp_if, and(3), tok(KW_IF), rule(test_nocond), opt_rule(comp_iter))
Expand Down
25 changes: 25 additions & 0 deletions tests/basics/annotate_var.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# test PEP 526, varible annotations

x: int
print("x" in globals())

x: int = 1
print(x)

t: tuple = 1, 2
print(t)

# a pure annotation in a function makes that variable local
def f():
x: int
try:
print(x)
except NameError:
print("NameError")
f()

# here, "x" should remain a global
def f():
x.y: int
print(x)
f()
5 changes: 5 additions & 0 deletions tests/basics/annotate_var.py.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
False
1
(1, 2)
NameError
1
1 change: 1 addition & 0 deletions tests/run-tests
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ def run_tests(pyb, tests, args, base_path="."):
skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from_close generator_name'.split()}) # require raise_varargs, generator name
skip_tests.update({'basics/async_%s.py' % t for t in 'with with2 with_break with_return'.split()}) # require async_with
skip_tests.update({'basics/%s.py' % t for t in 'try_reraise try_reraise2'.split()}) # require raise_varargs
skip_tests.add('basics/annotate_var.py') # requires checking for unbound local
skip_tests.add('basics/del_deref.py') # requires checking for unbound local
skip_tests.add('basics/del_local.py') # requires checking for unbound loc 441F al
skip_tests.add('basics/exception_chain.py') # raise from is not supported
Expand Down
0