diff --git a/py/compile.c b/py/compile.c index 28e51e9c7..f3ac5b383 100644 --- a/py/compile.c +++ b/py/compile.c @@ -33,9 +33,7 @@ #include "py/scope.h" #include "py/emit.h" #include "py/compile.h" -#include "py/smallint.h" #include "py/runtime.h" -#include "py/builtin.h" // TODO need to mangle __attr names @@ -124,238 +122,6 @@ STATIC void compile_syntax_error(compiler_t *comp, mp_parse_node_t pn, const cha } } -#if MICROPY_COMP_MODULE_CONST -STATIC const mp_map_elem_t mp_constants_table[] = { - #if MICROPY_PY_UCTYPES - { MP_OBJ_NEW_QSTR(MP_QSTR_uctypes), (mp_obj_t)&mp_module_uctypes }, - #endif - // Extra constants as defined by a port - MICROPY_PORT_CONSTANTS -}; -STATIC MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table); -#endif - -// this function is essentially a simple preprocessor -STATIC mp_parse_node_t fold_constants(compiler_t *comp, mp_parse_node_t pn, mp_map_t *consts) { - if (0) { - // dummy -#if MICROPY_COMP_CONST - } else if (MP_PARSE_NODE_IS_ID(pn)) { - // lookup identifier in table of dynamic constants - qstr qst = MP_PARSE_NODE_LEAF_ARG(pn); - mp_map_elem_t *elem = mp_map_lookup(consts, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); - if (elem != NULL) { - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, MP_OBJ_SMALL_INT_VALUE(elem->value)); - } -#endif - } else if (MP_PARSE_NODE_IS_STRUCT(pn)) { - mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn; - - // fold some parse nodes before folding their arguments - switch (MP_PARSE_NODE_STRUCT_KIND(pns)) { -#if MICROPY_COMP_CONST - case PN_expr_stmt: - if (!MP_PARSE_NODE_IS_NULL(pns->nodes[1])) { - if (!(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_expr_stmt_augassign) - || MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_expr_stmt_assign_list))) { - // this node is of the form = - if (MP_PARSE_NODE_IS_ID(pns->nodes[0]) - && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_power) - && MP_PARSE_NODE_IS_ID(((mp_parse_node_struct_t*)pns->nodes[1])->nodes[0]) - && MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t*)pns->nodes[1])->nodes[0]) == MP_QSTR_const - && MP_PARSE_NODE_IS_STRUCT_KIND(((mp_parse_node_struct_t*)pns->nodes[1])->nodes[1], PN_trailer_paren) - && MP_PARSE_NODE_IS_NULL(((mp_parse_node_struct_t*)pns->nodes[1])->nodes[2]) - ) { - // code to assign dynamic constants: id = const(value) - - // get the id - qstr id_qstr = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); - - // get the value - mp_parse_node_t pn_value = ((mp_parse_node_struct_t*)((mp_parse_node_struct_t*)pns->nodes[1])->nodes[1])->nodes[0]; - pn_value = fold_constants(comp, pn_value, consts); - if (!MP_PARSE_NODE_IS_SMALL_INT(pn_value)) { - compile_syntax_error(comp, (mp_parse_node_t)pns, "constant must be an integer"); - break; - } - mp_int_t value = MP_PARSE_NODE_LEAF_SMALL_INT(pn_value); - - // store the value in the table of dynamic constants - mp_map_elem_t *elem = mp_map_lookup(consts, MP_OBJ_NEW_QSTR(id_qstr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); - if (elem->value != MP_OBJ_NULL) { - compile_syntax_error(comp, (mp_parse_node_t)pns, "constant redefined"); - break; - } - elem->value = MP_OBJ_NEW_SMALL_INT(value); - - // replace const(value) with value - pns->nodes[1] = pn_value; - - // finished folding this assignment - return pn; - } - } - } - break; -#endif - case PN_string: - case PN_bytes: - case PN_const_object: - return pn; - } - - // fold arguments - int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); - for (int i = 0; i < n; i++) { - pns->nodes[i] = fold_constants(comp, pns->nodes[i], consts); - } - - // try to fold this parse node - switch (MP_PARSE_NODE_STRUCT_KIND(pns)) { - case PN_atom_paren: - if (n == 1 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0])) { - // (int) - pn = pns->nodes[0]; - } - break; - - case PN_expr: - if (n == 2 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[1])) { - // int | int - mp_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]); - mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[1]); - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 | arg1); - } - break; - - case PN_and_expr: - if (n == 2 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[1])) { - // int & int - mp_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]); - mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[1]); - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 & arg1); - } - break; - - case PN_shift_expr: - if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) { - mp_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]); - mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[2]); - if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_LESS)) { - // int << int - if (!(arg1 >= (mp_int_t)BITS_PER_WORD || arg0 > (MP_SMALL_INT_MAX >> arg1) || arg0 < (MP_SMALL_INT_MIN >> arg1))) { - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 << arg1); - } - } else { - assert(MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_MORE)); // should be - // int >> int - if (arg1 >= (mp_int_t)BITS_PER_WORD) { - // Shifting to big amounts is underfined behavior - // in C and is CPU-dependent; propagate sign bit. - arg1 = BITS_PER_WORD - 1; - } - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 >> arg1); - } - } - break; - - case PN_arith_expr: - // overflow checking here relies on SMALL_INT being strictly smaller than mp_int_t - if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) { - mp_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]); - mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[2]); - if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PLUS)) { - // int + int - arg0 += arg1; - } else { - assert(MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_MINUS)); // should be - // int - int - arg0 -= arg1; - } - if (MP_SMALL_INT_FITS(arg0)) { - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0); - } - } - break; - - case PN_term: - if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) { - mp_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]); - mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[2]); - if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_STAR)) { - // int * int - if (!mp_small_int_mul_overflow(arg0, arg1)) { - arg0 *= arg1; - if (MP_SMALL_INT_FITS(arg0)) { - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0); - } - } - } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_SLASH)) { - // int / int - // pass - } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PERCENT)) { - // int%int - if (arg1 != 0) { - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, mp_small_int_modulo(arg0, arg1)); - } - } else { - assert(MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_SLASH)); // should be - if (arg1 != 0) { - // int // int - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, mp_small_int_floor_divide(arg0, arg1)); - } - } - } - break; - - case PN_factor_2: - if (MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[1])) { - mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[1]); - if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_PLUS)) { - // +int - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg); - } else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_MINUS)) { - // -int - arg = -arg; - if (MP_SMALL_INT_FITS(arg)) { - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg); - } - } else { - assert(MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_TILDE)); // should be - // ~int - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, ~arg); - } - } - break; - - case PN_power: - if (0) { -#if MICROPY_COMP_MODULE_CONST - } else if (MP_PARSE_NODE_IS_ID(pns->nodes[0]) && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_trailer_period) && MP_PARSE_NODE_IS_NULL(pns->nodes[2])) { - // id.id - // look it up in constant table, see if it can be replaced with an integer - mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1]; - assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0])); - qstr q_base = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); - qstr q_attr = MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]); - mp_map_elem_t *elem = mp_map_lookup((mp_map_t*)&mp_constants_map, MP_OBJ_NEW_QSTR(q_base), MP_MAP_LOOKUP); - if (elem != NULL) { - mp_obj_t dest[2]; - mp_load_method_maybe(elem->value, q_attr, dest); - if (MP_OBJ_IS_SMALL_INT(dest[0]) && dest[1] == NULL) { - mp_int_t val = MP_OBJ_SMALL_INT_VALUE(dest[0]); - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, val); - } - } -#endif - } - break; - } - } - - return pn; -} - STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_arglist, bool is_method_call, int n_positional_extra); STATIC void compile_comprehension(compiler_t *comp, mp_parse_node_struct_t *pns, scope_kind_t kind); STATIC void compile_node(compiler_t *comp, mp_parse_node_t pn); @@ -3332,13 +3098,6 @@ mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt // create the module scope scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, parse_tree->root, emit_opt); - // optimise constants (scope must be set for error messages to work) - comp->scope_cur = module_scope; - mp_map_t consts; - mp_map_init(&consts, 0); - module_scope->pn = fold_constants(comp, module_scope->pn, &consts); - mp_map_deinit(&consts); - // create standard emitter; it's used at least for MP_PASS_SCOPE emit_t *emit_bc = emit_bc_new(); diff --git a/py/mpconfig.h b/py/mpconfig.h index 4b986eef9..500af5e76 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -215,6 +215,11 @@ /*****************************************************************************/ /* Compiler configuration */ +// Whether to enable constant folding; eg 1+2 rewritten as 3 +#ifndef MICROPY_COMP_CONST_FOLDING +#define MICROPY_COMP_CONST_FOLDING (1) +#endif + // Whether to enable lookup of constants in modules; eg module.CONST #ifndef MICROPY_COMP_MODULE_CONST #define MICROPY_COMP_MODULE_CONST (0) diff --git a/py/parse.c b/py/parse.c index 64acaec44..759c1c364 100644 --- a/py/parse.c +++ b/py/parse.c @@ -35,6 +35,8 @@ #include "py/parse.h" #include "py/parsenum.h" #include "py/smallint.h" +#include "py/runtime.h" +#include "py/builtin.h" #define RULE_ACT_ARG_MASK (0x0f) #define RULE_ACT_KIND_MASK (0x30) @@ -121,8 +123,14 @@ typedef struct _mp_parse_chunk_t { byte data[]; } mp_parse_chunk_t; +typedef enum { + PARSE_ERROR_NONE = 0, + PARSE_ERROR_MEMORY, + PARSE_ERROR_CONST, +} parse_error_t; + typedef struct _parser_t { - bool had_memory_error; + parse_error_t parse_error; mp_uint_t rule_stack_alloc; mp_uint_t rule_stack_top; @@ -136,11 +144,11 @@ typedef struct _parser_t { mp_parse_tree_t tree; mp_parse_chunk_t *cur_chunk; -} parser_t; -STATIC inline void memory_error(parser_t *parser) { - parser->had_memory_error = true; -} + #if MICROPY_COMP_CONST + mp_map_t consts; + #endif +} parser_t; STATIC void *parser_alloc(parser_t *parser, size_t num_bytes) { // use a custom memory allocator to store parse nodes sequentially in large chunks @@ -184,13 +192,13 @@ STATIC void *parser_alloc(parser_t *parser, size_t num_bytes) { } STATIC void push_rule(parser_t *parser, mp_uint_t src_line, const rule_t *rule, mp_uint_t arg_i) { - if (parser->had_memory_error) { + if (parser->parse_error) { return; } if (parser->rule_stack_top >= parser->rule_stack_alloc) { rule_stack_t *rs = m_renew_maybe(rule_stack_t, parser->rule_stack, parser->rule_stack_alloc, parser->rule_stack_alloc + MICROPY_ALLOC_PARSE_RULE_INC, true); if (rs == NULL) { - memory_error(parser); + parser->parse_error = PARSE_ERROR_MEMORY; return; } parser->rule_stack = rs; @@ -210,7 +218,7 @@ STATIC void push_rule_from_arg(parser_t *parser, mp_uint_t arg) { } STATIC void pop_rule(parser_t *parser, const rule_t **rule, mp_uint_t *arg_i, mp_uint_t *src_line) { - assert(!parser->had_memory_error); + assert(!parser->parse_error); parser->rule_stack_top -= 1; *rule = rules[parser->rule_stack[parser->rule_stack_top].rule_id]; *arg_i = parser->rule_stack[parser->rule_stack_top].arg_i; @@ -301,7 +309,7 @@ STATIC void result_stack_show(parser_t *parser) { */ STATIC mp_parse_node_t pop_result(parser_t *parser) { - if (parser->had_memory_error) { + if (parser->parse_error) { return MP_PARSE_NODE_NULL; } assert(parser->result_stack_top > 0); @@ -309,7 +317,7 @@ STATIC mp_parse_node_t pop_result(parser_t *parser) { } STATIC mp_parse_node_t peek_result(parser_t *parser, mp_uint_t pos) { - if (parser->had_memory_error) { + if (parser->parse_error) { return MP_PARSE_NODE_NULL; } assert(parser->result_stack_top > pos); @@ -317,13 +325,13 @@ STATIC mp_parse_node_t peek_result(parser_t *parser, mp_uint_t pos) { } STATIC void push_result_node(parser_t *parser, mp_parse_node_t pn) { - if (parser->had_memory_error) { + if (parser->parse_error) { return; } if (parser->result_stack_top >= parser->result_stack_alloc) { mp_parse_node_t *stack = m_renew_maybe(mp_parse_node_t, parser->result_stack, parser->result_stack_alloc, parser->result_stack_alloc + MICROPY_ALLOC_PARSE_RESULT_INC, true); if (stack == NULL) { - memory_error(parser); + parser->parse_error = PARSE_ERROR_MEMORY; return; } parser->result_stack = stack; @@ -335,7 +343,7 @@ STATIC void push_result_node(parser_t *parser, mp_parse_node_t pn) { STATIC mp_parse_node_t make_node_string_bytes(parser_t *parser, mp_uint_t src_line, mp_uint_t rule_kind, const char *str, mp_uint_t len) { mp_parse_node_struct_t *pn = parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t) * 2); if (pn == NULL) { - memory_error(parser); + parser->parse_error = PARSE_ERROR_MEMORY; return MP_PARSE_NODE_NULL; } pn->source_line = src_line; @@ -350,7 +358,7 @@ STATIC mp_parse_node_t make_node_string_bytes(parser_t *parser, mp_uint_t src_li STATIC mp_parse_node_t make_node_const_object(parser_t *parser, mp_uint_t src_line, mp_obj_t obj) { mp_parse_node_struct_t *pn = parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t)); if (pn == NULL) { - memory_error(parser); + parser->parse_error = PARSE_ERROR_MEMORY; return MP_PARSE_NODE_NULL; } pn->source_line = src_line; @@ -363,7 +371,17 @@ STATIC void push_result_token(parser_t *parser) { mp_parse_node_t pn; mp_lexer_t *lex = parser->lexer; if (lex->tok_kind == MP_TOKEN_NAME) { - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, qstr_from_strn(lex->vstr.buf, lex->vstr.len)); + qstr id = qstr_from_strn(lex->vstr.buf, lex->vstr.len); + #if MICROPY_COMP_CONST + // lookup identifier in table of dynamic constants + mp_map_elem_t *elem = mp_map_lookup(&parser->consts, MP_OBJ_NEW_QSTR(id), MP_MAP_LOOKUP); + if (elem != NULL) { + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, MP_OBJ_SMALL_INT_VALUE(elem->value)); + } else + #endif + { + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, id); + } } else if (lex->tok_kind == MP_TOKEN_INTEGER) { mp_obj_t o = mp_parse_num_integer(lex->vstr.buf, lex->vstr.len, 0, lex); if (MP_OBJ_IS_SMALL_INT(o)) { @@ -398,10 +416,234 @@ STATIC void push_result_token(parser_t *parser) { push_result_node(parser, pn); } +#if MICROPY_COMP_MODULE_CONST +STATIC const mp_map_elem_t mp_constants_table[] = { + #if MICROPY_PY_UCTYPES + { MP_OBJ_NEW_QSTR(MP_QSTR_uctypes), (mp_obj_t)&mp_module_uctypes }, + #endif + // Extra constants as defined by a port + MICROPY_PORT_CONSTANTS +}; +STATIC MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table); +#endif + +#if MICROPY_COMP_CONST_FOLDING +STATIC bool fold_constants(parser_t *parser, const rule_t *rule, mp_uint_t num_args) { + // this code does folding of arbitrary integer expressions, eg 1 + 2 * 3 + 4 + // it does not do partial folding, eg 1 + 2 + x -> 3 + x + + mp_int_t arg0; + if (rule->rule_id == RULE_expr + || rule->rule_id == RULE_xor_expr + || rule->rule_id == RULE_and_expr) { + // folding for binary ops: | ^ & + mp_parse_node_t pn = peek_result(parser, num_args - 1); + if (!MP_PARSE_NODE_IS_SMALL_INT(pn)) { + return false; + } + arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pn); + for (mp_int_t i = num_args - 2; i >= 0; --i) { + pn = peek_result(parser, i); + if (!MP_PARSE_NODE_IS_SMALL_INT(pn)) { + return false; + } + mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pn); + if (rule->rule_id == RULE_expr) { + // int | int + arg0 |= arg1; + } else if (rule->rule_id == RULE_xor_expr) { + // int ^ int + arg0 ^= arg1; + } else if (rule->rule_id == RULE_and_expr) { + // int & int + arg0 &= arg1; + } + } + } else if (rule->rule_id == RULE_shift_expr + || rule->rule_id == RULE_arith_expr + || rule->rule_id == RULE_term) { + // folding for binary ops: << >> + - * / % // + mp_parse_node_t pn = peek_result(parser, num_args - 1); + if (!MP_PARSE_NODE_IS_SMALL_INT(pn)) { + return false; + } + arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pn); + for (mp_int_t i = num_args - 2; i >= 1; i -= 2) { + pn = peek_result(parser, i - 1); + if (!MP_PARSE_NODE_IS_SMALL_INT(pn)) { + return false; + } + mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pn); + mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, i)); + if (tok == MP_TOKEN_OP_DBL_LESS) { + // int << int + if (arg1 >= (mp_int_t)BITS_PER_WORD + || arg0 > (MP_SMALL_INT_MAX >> arg1) + || arg0 < (MP_SMALL_INT_MIN >> arg1)) { + return false; + } + arg0 <<= arg1; + } else if (tok == MP_TOKEN_OP_DBL_MORE) { + // int >> int + if (arg1 >= (mp_int_t)BITS_PER_WORD) { + // Shifting to big amounts is underfined behavior + // in C and is CPU-dependent; propagate sign bit. + arg1 = BITS_PER_WORD - 1; + } + arg0 >>= arg1; + } else if (tok == MP_TOKEN_OP_PLUS) { + // int + int + arg0 += arg1; + } else if (tok == MP_TOKEN_OP_MINUS) { + // int - int + arg0 -= arg1; + } else if (tok == MP_TOKEN_OP_STAR) { + // int * int + if (mp_small_int_mul_overflow(arg0, arg1)) { + return false; + } + arg0 *= arg1; + } else if (tok == MP_TOKEN_OP_SLASH) { + // int / int + return false; + } else if (tok == MP_TOKEN_OP_PERCENT) { + // int % int + if (arg1 == 0) { + return false; + } + arg0 = mp_small_int_modulo(arg0, arg1); + } else { + assert(tok == MP_TOKEN_OP_DBL_SLASH); // should be + // int // int + if (arg1 == 0) { + return false; + } + arg0 = mp_small_int_floor_divide(arg0, arg1); + } + if (!MP_SMALL_INT_FITS(arg0)) { + return false; + } + } + } else if (rule->rule_id == RULE_factor_2) { + // folding for unary ops: + - ~ + mp_parse_node_t pn = peek_result(parser, 0); + if (!MP_PARSE_NODE_IS_SMALL_INT(pn)) { + return false; + } + arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pn); + mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, 1)); + if (tok == MP_TOKEN_OP_PLUS) { + // +int + } else if (tok == MP_TOKEN_OP_MINUS) { + // -int + arg0 = -arg0; + if (!MP_SMALL_INT_FITS(arg0)) { + return false; + } + } else { + assert(tok == MP_TOKEN_OP_TILDE); // should be + // ~int + arg0 = ~arg0; + } + + #if MICROPY_COMP_CONST + } else if (rule->rule_id == RULE_expr_stmt) { + mp_parse_node_t pn1 = peek_result(parser, 0); + if (!MP_PARSE_NODE_IS_NULL(pn1) + && !(MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_expr_stmt_augassign) + || MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_expr_stmt_assign_list))) { + // this node is of the form = + mp_parse_node_t pn0 = peek_result(parser, 1); + if (MP_PARSE_NODE_IS_ID(pn0) + && MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_power) + && MP_PARSE_NODE_IS_ID(((mp_parse_node_struct_t*)pn1)->nodes[0]) + && MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t*)pn1)->nodes[0]) == MP_QSTR_const + && MP_PARSE_NODE_IS_STRUCT_KIND(((mp_parse_node_struct_t*)pn1)->nodes[1], RULE_trailer_paren) + && MP_PARSE_NODE_IS_NULL(((mp_parse_node_struct_t*)pn1)->nodes[2]) + ) { + // code to assign dynamic constants: id = const(value) + + // get the id + qstr id = MP_PARSE_NODE_LEAF_ARG(pn0); + + // get the value + mp_parse_node_t pn_value = ((mp_parse_node_struct_t*)((mp_parse_node_struct_t*)pn1)->nodes[1])->nodes[0]; + if (!MP_PARSE_NODE_IS_SMALL_INT(pn_value)) { + parser->parse_error = PARSE_ERROR_CONST; + return false; + } + mp_int_t value = MP_PARSE_NODE_LEAF_SMALL_INT(pn_value); + + // store the value in the table of dynamic constants + mp_map_elem_t *elem = mp_map_lookup(&parser->consts, MP_OBJ_NEW_QSTR(id), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + assert(elem->value == MP_OBJ_NULL); + elem->value = MP_OBJ_NEW_SMALL_INT(value); + + // replace const(value) with value + pop_result(parser); + push_result_node(parser, pn_value); + + // finished folding this assignment, but we still want it to be part of the tree + return false; + } + } + return false; + #endif + + #if MICROPY_COMP_MODULE_CONST + } else if (rule->rule_id == RULE_power) { + mp_parse_node_t pn0 = peek_result(parser, 2); + mp_parse_node_t pn1 = peek_result(parser, 1); + mp_parse_node_t pn2 = peek_result(parser, 0); + if (!(MP_PARSE_NODE_IS_ID(pn0) + && MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_trailer_period) + && MP_PARSE_NODE_IS_NULL(pn2))) { + return false; + } + // id1.id2 + // look it up in constant table, see if it can be replaced with an integer + mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pn1; + assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0])); + qstr q_base = MP_PARSE_NODE_LEAF_ARG(pn0); + qstr q_attr = MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]); + mp_map_elem_t *elem = mp_map_lookup((mp_map_t*)&mp_constants_map, MP_OBJ_NEW_QSTR(q_base), MP_MAP_LOOKUP); + if (elem == NULL) { + return false; + } + mp_obj_t dest[2]; + mp_load_method_maybe(elem->value, q_attr, dest); + if (!(MP_OBJ_IS_SMALL_INT(dest[0]) && dest[1] == NULL)) { + return false; + } + arg0 = MP_OBJ_SMALL_INT_VALUE(dest[0]); + #endif + + } else { + return false; + } + + // success folding this rule + + for (mp_uint_t i = num_args; i > 0; i--) { + pop_result(parser); + } + push_result_node(parser, mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0)); + + return true; +} +#endif + STATIC void push_result_rule(parser_t *parser, mp_uint_t src_line, const rule_t *rule, mp_uint_t num_args) { + #if MICROPY_COMP_CONST_FOLDING + if (fold_constants(parser, rule, num_args)) { + // we folded this rule so return straight away + return; + } + #endif + mp_parse_node_struct_t *pn = parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t) * num_args); if (pn == NULL) { - memory_error(parser); + parser->parse_error = PARSE_ERROR_MEMORY; return; } pn->source_line = src_line; @@ -418,7 +660,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { parser_t parser; - parser.had_memory_error = false; + parser.parse_error = PARSE_ERROR_NONE; parser.rule_stack_alloc = MICROPY_ALLOC_PARSE_RULE_INIT; parser.rule_stack_top = 0; @@ -433,6 +675,10 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { parser.tree.chunk = NULL; parser.cur_chunk = NULL; + #if MICROPY_COMP_CONST + mp_map_init(&parser.consts, 0); + #endif + // check if we could allocate the stacks if (parser.rule_stack == NULL || parser.result_stack == NULL) { goto memory_error; @@ -456,7 +702,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { for (;;) { next_rule: - if (parser.rule_stack_top == 0 || parser.had_memory_error) { + if (parser.rule_stack_top == 0 || parser.parse_error) { break; } @@ -737,6 +983,10 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } } + #if MICROPY_COMP_CONST + mp_map_deinit(&parser.consts); + #endif + // truncate final chunk and link into chain of chunks if (parser.cur_chunk != NULL) { (void)m_renew(byte, parser.cur_chunk, @@ -749,11 +999,19 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { mp_obj_t exc; - // check if we had a memory error - if (parser.had_memory_error) { -memory_error: - exc = mp_obj_new_exception_msg(&mp_type_MemoryError, - "parser could not allocate enough memory"); + if (parser.parse_error) { + #if MICROPY_COMP_CONST + if (parser.parse_error == PARSE_ERROR_CONST) { + exc = mp_obj_new_exception_msg(&mp_type_SyntaxError, + "constant must be an integer"); + } else + #endif + { + assert(parser.parse_error == PARSE_ERROR_MEMORY); + memory_error: + exc = mp_obj_new_exception_msg(&mp_type_MemoryError, + "parser could not allocate enough memory"); + } parser.tree.root = MP_PARSE_NODE_NULL; goto finished; }