eb7bfcb286
Qstr's are now split into a linked-list of qstr pools. This has 2 benefits: the first pool can be in ROM (huge benefit, since we no longer use RAM for the core qstrs), and subsequent pools use m_new for the next pool instead of m_renew (thus avoiding a huge single table for all the qstrs). Still would be better to use a hash table, but this scheme takes us part of the way (eventually convert the pools to hash tables). Also fixed bug with import. Also improved the way the module code is referenced (not magic number 1 anymore).
3146 lines
120 KiB
C
3146 lines
120 KiB
C
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "misc.h"
|
|
#include "mpconfig.h"
|
|
#include "mpqstr.h"
|
|
#include "lexer.h"
|
|
#include "parse.h"
|
|
#include "scope.h"
|
|
#include "runtime0.h"
|
|
#include "emit.h"
|
|
#include "obj.h"
|
|
#include "compile.h"
|
|
#include "runtime.h"
|
|
|
|
// TODO need to mangle __attr names
|
|
|
|
#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_THUMB)
|
|
|
|
typedef enum {
|
|
PN_none = 0,
|
|
#define DEF_RULE(rule, comp, kind, arg...) PN_##rule,
|
|
#include "grammar.h"
|
|
#undef DEF_RULE
|
|
PN_maximum_number_of,
|
|
} pn_kind_t;
|
|
|
|
#define EMIT(fun, arg...) (comp->emit_method_table->fun(comp->emit, ##arg))
|
|
#define EMIT_INLINE_ASM(fun, arg...) (comp->emit_inline_asm_method_table->fun(comp->emit_inline_asm, ##arg))
|
|
|
|
#define EMIT_OPT_NONE (0)
|
|
#define EMIT_OPT_BYTE_CODE (1)
|
|
#define EMIT_OPT_NATIVE_PYTHON (2)
|
|
#define EMIT_OPT_VIPER (3)
|
|
#define EMIT_OPT_ASM_THUMB (4)
|
|
|
|
typedef struct _compiler_t {
|
|
bool is_repl;
|
|
pass_kind_t pass;
|
|
bool had_error; // try to keep compiler clean from nlr
|
|
|
|
int next_label;
|
|
|
|
int break_label;
|
|
int continue_label;
|
|
int except_nest_level;
|
|
|
|
int n_arg_keyword;
|
|
bool have_star_arg;
|
|
bool have_dbl_star_arg;
|
|
bool have_bare_star;
|
|
int param_pass;
|
|
int param_pass_num_dict_params;
|
|
int param_pass_num_default_params;
|
|
|
|
scope_t *scope_head;
|
|
scope_t *scope_cur;
|
|
|
|
emit_t *emit; // current emitter
|
|
const emit_method_table_t *emit_method_table; // current emit method table
|
|
|
|
emit_inline_asm_t *emit_inline_asm; // current emitter for inline asm
|
|
const emit_inline_asm_method_table_t *emit_inline_asm_method_table; // current emit method table for inline asm
|
|
} compiler_t;
|
|
|
|
mp_parse_node_t fold_constants(mp_parse_node_t pn) {
|
|
if (MP_PARSE_NODE_IS_STRUCT(pn)) {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
|
|
// fold arguments first
|
|
for (int i = 0; i < n; i++) {
|
|
pns->nodes[i] = fold_constants(pns->nodes[i]);
|
|
}
|
|
|
|
switch (MP_PARSE_NODE_STRUCT_KIND(pns)) {
|
|
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])) {
|
|
int arg0 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
|
|
int arg1 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[2]);
|
|
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_LESS)) {
|
|
#if MICROPY_EMIT_CPYTHON
|
|
// can overflow; enabled only to compare with CPython
|
|
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 << arg1);
|
|
#endif
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_MORE)) {
|
|
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 >> arg1);
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PN_arith_expr:
|
|
// overflow checking here relies on SMALL_INT being strictly smaller than machine_int_t
|
|
if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
|
|
machine_int_t arg0 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
|
|
machine_int_t arg1 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[2]);
|
|
machine_int_t res;
|
|
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PLUS)) {
|
|
res = arg0 + arg1;
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_MINUS)) {
|
|
res = arg0 - arg1;
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
res = 0;
|
|
}
|
|
if (MP_FIT_SMALL_INT(res)) {
|
|
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, res);
|
|
}
|
|
}
|
|
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])) {
|
|
int arg0 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
|
|
int arg1 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[2]);
|
|
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_STAR)) {
|
|
#if MICROPY_EMIT_CPYTHON
|
|
// can overflow; enabled only to compare with CPython
|
|
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 * arg1);
|
|
#endif
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_SLASH)) {
|
|
; // pass
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PERCENT)) {
|
|
// XXX implement this properly as Python's % operator acts differently to C's
|
|
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 % arg1);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_SLASH)) {
|
|
// XXX implement this properly as Python's // operator acts differently to C's
|
|
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 / arg1);
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PN_factor_2:
|
|
if (MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[1])) {
|
|
machine_int_t arg = MP_PARSE_NODE_LEAF_ARG(pns->nodes[1]);
|
|
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_PLUS)) {
|
|
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)) {
|
|
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_TILDE)) {
|
|
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, ~arg);
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
#if MICROPY_EMIT_CPYTHON
|
|
case PN_power:
|
|
// can overflow; enabled only to compare with CPython
|
|
if (MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_NULL(pns->nodes[1]) && !MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
|
|
mp_parse_node_struct_t* pns2 = (mp_parse_node_struct_t*)pns->nodes[2];
|
|
if (MP_PARSE_NODE_IS_SMALL_INT(pns2->nodes[0])) {
|
|
int power = MP_PARSE_NODE_LEAF_ARG(pns2->nodes[0]);
|
|
if (power >= 0) {
|
|
int ans = 1;
|
|
int base = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
|
|
for (; power > 0; power--) {
|
|
ans *= base;
|
|
}
|
|
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, ans);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return pn;
|
|
}
|
|
|
|
void compile_node(compiler_t *comp, mp_parse_node_t pn);
|
|
|
|
static int comp_next_label(compiler_t *comp) {
|
|
return comp->next_label++;
|
|
}
|
|
|
|
static scope_t *scope_new_and_link(compiler_t *comp, scope_kind_t kind, mp_parse_node_t pn, uint emit_options) {
|
|
scope_t *scope = scope_new(kind, pn, rt_get_unique_code_id(), emit_options);
|
|
scope->parent = comp->scope_cur;
|
|
scope->next = NULL;
|
|
if (comp->scope_head == NULL) {
|
|
comp->scope_head = scope;
|
|
} else {
|
|
scope_t *s = comp->scope_head;
|
|
while (s->next != NULL) {
|
|
s = s->next;
|
|
}
|
|
s->next = scope;
|
|
}
|
|
return scope;
|
|
}
|
|
|
|
static int list_len(mp_parse_node_t pn, int pn_kind) {
|
|
if (MP_PARSE_NODE_IS_NULL(pn)) {
|
|
return 0;
|
|
} else if (MP_PARSE_NODE_IS_LEAF(pn)) {
|
|
return 1;
|
|
} else {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns) != pn_kind) {
|
|
return 1;
|
|
} else {
|
|
return MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void apply_to_single_or_list(compiler_t *comp, mp_parse_node_t pn, int pn_list_kind, void (*f)(compiler_t*, mp_parse_node_t)) {
|
|
if (MP_PARSE_NODE_IS_STRUCT(pn) && MP_PARSE_NODE_STRUCT_KIND((mp_parse_node_struct_t*)pn) == pn_list_kind) {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
for (int i = 0; i < num_nodes; i++) {
|
|
f(comp, pns->nodes[i]);
|
|
}
|
|
} else if (!MP_PARSE_NODE_IS_NULL(pn)) {
|
|
f(comp, pn);
|
|
}
|
|
}
|
|
|
|
static int list_get(mp_parse_node_t *pn, int pn_kind, mp_parse_node_t **nodes) {
|
|
if (MP_PARSE_NODE_IS_NULL(*pn)) {
|
|
*nodes = NULL;
|
|
return 0;
|
|
} else if (MP_PARSE_NODE_IS_LEAF(*pn)) {
|
|
*nodes = pn;
|
|
return 1;
|
|
} else {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)(*pn);
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns) != pn_kind) {
|
|
*nodes = pn;
|
|
return 1;
|
|
} else {
|
|
*nodes = pns->nodes;
|
|
return MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
}
|
|
}
|
|
}
|
|
|
|
void compile_do_nothing(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
}
|
|
|
|
void compile_generic_all_nodes(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
for (int i = 0; i < num_nodes; i++) {
|
|
compile_node(comp, pns->nodes[i]);
|
|
}
|
|
}
|
|
|
|
#if MICROPY_EMIT_CPYTHON
|
|
static bool cpython_c_tuple_is_const(mp_parse_node_t pn) {
|
|
if (!MP_PARSE_NODE_IS_LEAF(pn)) {
|
|
return false;
|
|
}
|
|
if (MP_PARSE_NODE_IS_ID(pn)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void cpython_c_print_quoted_str(vstr_t *vstr, qstr qstr, bool bytes) {
|
|
const char *str = qstr_str(qstr);
|
|
int len = strlen(str);
|
|
bool has_single_quote = false;
|
|
bool has_double_quote = false;
|
|
for (int i = 0; i < len; i++) {
|
|
if (str[i] == '\'') {
|
|
has_single_quote = true;
|
|
} else if (str[i] == '"') {
|
|
has_double_quote = true;
|
|
}
|
|
}
|
|
if (bytes) {
|
|
vstr_printf(vstr, "b");
|
|
}
|
|
bool quote_single = false;
|
|
if (has_single_quote && !has_double_quote) {
|
|
vstr_printf(vstr, "\"");
|
|
} else {
|
|
quote_single = true;
|
|
vstr_printf(vstr, "'");
|
|
}
|
|
for (int i = 0; i < len; i++) {
|
|
if (str[i] == '\n') {
|
|
vstr_printf(vstr, "\\n");
|
|
} else if (str[i] == '\\') {
|
|
vstr_printf(vstr, "\\\\");
|
|
} else if (str[i] == '\'' && quote_single) {
|
|
vstr_printf(vstr, "\\'");
|
|
} else {
|
|
vstr_printf(vstr, "%c", str[i]);
|
|
}
|
|
}
|
|
if (has_single_quote && !has_double_quote) {
|
|
vstr_printf(vstr, "\"");
|
|
} else {
|
|
vstr_printf(vstr, "'");
|
|
}
|
|
}
|
|
|
|
static void cpython_c_tuple_emit_const(compiler_t *comp, mp_parse_node_t pn, vstr_t *vstr) {
|
|
assert(MP_PARSE_NODE_IS_LEAF(pn));
|
|
int arg = MP_PARSE_NODE_LEAF_ARG(pn);
|
|
switch (MP_PARSE_NODE_LEAF_KIND(pn)) {
|
|
case MP_PARSE_NODE_ID: assert(0);
|
|
case MP_PARSE_NODE_SMALL_INT: vstr_printf(vstr, "%d", arg); break;
|
|
case MP_PARSE_NODE_INTEGER: vstr_printf(vstr, "%s", qstr_str(arg)); break;
|
|
case MP_PARSE_NODE_DECIMAL: vstr_printf(vstr, "%s", qstr_str(arg)); break;
|
|
case MP_PARSE_NODE_STRING: cpython_c_print_quoted_str(vstr, arg, false); break;
|
|
case MP_PARSE_NODE_BYTES: cpython_c_print_quoted_str(vstr, arg, true); break;
|
|
case MP_PARSE_NODE_TOKEN:
|
|
switch (arg) {
|
|
case MP_TOKEN_KW_FALSE: vstr_printf(vstr, "False"); break;
|
|
case MP_TOKEN_KW_NONE: vstr_printf(vstr, "None"); break;
|
|
case MP_TOKEN_KW_TRUE: vstr_printf(vstr, "True"); break;
|
|
default: assert(0);
|
|
}
|
|
break;
|
|
default: assert(0);
|
|
}
|
|
}
|
|
|
|
static void cpython_c_tuple(compiler_t *comp, mp_parse_node_t pn, mp_parse_node_struct_t *pns_list) {
|
|
int n = 0;
|
|
if (pns_list != NULL) {
|
|
n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_list);
|
|
}
|
|
int total = n;
|
|
bool is_const = true;
|
|
if (!MP_PARSE_NODE_IS_NULL(pn)) {
|
|
total += 1;
|
|
if (!cpython_c_tuple_is_const(pn)) {
|
|
is_const = false;
|
|
}
|
|
}
|
|
for (int i = 0; i < n; i++) {
|
|
if (!cpython_c_tuple_is_const(pns_list->nodes[i])) {
|
|
is_const = false;
|
|
break;
|
|
}
|
|
}
|
|
if (total > 0 && is_const) {
|
|
bool need_comma = false;
|
|
vstr_t *vstr = vstr_new();
|
|
vstr_printf(vstr, "(");
|
|
if (!MP_PARSE_NODE_IS_NULL(pn)) {
|
|
cpython_c_tuple_emit_const(comp, pn, vstr);
|
|
need_comma = true;
|
|
}
|
|
for (int i = 0; i < n; i++) {
|
|
if (need_comma) {
|
|
vstr_printf(vstr, ", ");
|
|
}
|
|
cpython_c_tuple_emit_const(comp, pns_list->nodes[i], vstr);
|
|
need_comma = true;
|
|
}
|
|
if (total == 1) {
|
|
vstr_printf(vstr, ",)");
|
|
} else {
|
|
vstr_printf(vstr, ")");
|
|
}
|
|
EMIT(load_const_verbatim_str, vstr_str(vstr));
|
|
vstr_free(vstr);
|
|
} else {
|
|
if (!MP_PARSE_NODE_IS_NULL(pn)) {
|
|
compile_node(comp, pn);
|
|
}
|
|
for (int i = 0; i < n; i++) {
|
|
compile_node(comp, pns_list->nodes[i]);
|
|
}
|
|
EMIT(build_tuple, total);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// funnelling all tuple creations through this function is purely so we can optionally agree with CPython
|
|
void c_tuple(compiler_t *comp, mp_parse_node_t pn, mp_parse_node_struct_t *pns_list) {
|
|
#if MICROPY_EMIT_CPYTHON
|
|
cpython_c_tuple(comp, pn, pns_list);
|
|
#else
|
|
int total = 0;
|
|
if (!MP_PARSE_NODE_IS_NULL(pn)) {
|
|
compile_node(comp, pn);
|
|
total += 1;
|
|
}
|
|
if (pns_list != NULL) {
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_list);
|
|
for (int i = 0; i < n; i++) {
|
|
compile_node(comp, pns_list->nodes[i]);
|
|
}
|
|
total += n;
|
|
}
|
|
EMIT(build_tuple, total);
|
|
#endif
|
|
}
|
|
|
|
void compile_generic_tuple(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
// a simple tuple expression
|
|
c_tuple(comp, MP_PARSE_NODE_NULL, pns);
|
|
}
|
|
|
|
static bool node_is_const_false(mp_parse_node_t pn) {
|
|
return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_FALSE);
|
|
// untested: || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_ARG(pn) == 1);
|
|
}
|
|
|
|
static bool node_is_const_true(mp_parse_node_t pn) {
|
|
return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_TRUE) || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_ARG(pn) == 1);
|
|
}
|
|
|
|
#if MICROPY_EMIT_CPYTHON
|
|
// the is_nested variable is purely to match with CPython, which doesn't fully optimise not's
|
|
static void cpython_c_if_cond(compiler_t *comp, mp_parse_node_t pn, bool jump_if, int label, bool is_nested) {
|
|
if (node_is_const_false(pn)) {
|
|
if (jump_if == false) {
|
|
EMIT(jump, label);
|
|
}
|
|
return;
|
|
} else if (node_is_const_true(pn)) {
|
|
if (jump_if == true) {
|
|
EMIT(jump, label);
|
|
}
|
|
return;
|
|
} else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_or_test) {
|
|
if (jump_if == false) {
|
|
int label2 = comp_next_label(comp);
|
|
for (int i = 0; i < n - 1; i++) {
|
|
cpython_c_if_cond(comp, pns->nodes[i], true, label2, true);
|
|
}
|
|
cpython_c_if_cond(comp, pns->nodes[n - 1], false, label, true);
|
|
EMIT(label_assign, label2);
|
|
} else {
|
|
for (int i = 0; i < n; i++) {
|
|
cpython_c_if_cond(comp, pns->nodes[i], true, label, true);
|
|
}
|
|
}
|
|
return;
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_and_test) {
|
|
if (jump_if == false) {
|
|
for (int i = 0; i < n; i++) {
|
|
cpython_c_if_cond(comp, pns->nodes[i], false, label, true);
|
|
}
|
|
} else {
|
|
int label2 = comp_next_label(comp);
|
|
for (int i = 0; i < n - 1; i++) {
|
|
cpython_c_if_cond(comp, pns->nodes[i], false, label2, true);
|
|
}
|
|
cpython_c_if_cond(comp, pns->nodes[n - 1], true, label, true);
|
|
EMIT(label_assign, label2);
|
|
}
|
|
return;
|
|
} else if (!is_nested && MP_PARSE_NODE_STRUCT_KIND(pns) == PN_not_test_2) {
|
|
cpython_c_if_cond(comp, pns->nodes[0], !jump_if, label, true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// nothing special, fall back to default compiling for node and jump
|
|
compile_node(comp, pn);
|
|
if (jump_if == false) {
|
|
EMIT(pop_jump_if_false, label);
|
|
} else {
|
|
EMIT(pop_jump_if_true, label);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void c_if_cond(compiler_t *comp, mp_parse_node_t pn, bool jump_if, int label) {
|
|
#if MICROPY_EMIT_CPYTHON
|
|
cpython_c_if_cond(comp, pn, jump_if, label, false);
|
|
#else
|
|
if (node_is_const_false(pn)) {
|
|
if (jump_if == false) {
|
|
EMIT(jump, label);
|
|
}
|
|
return;
|
|
} else if (node_is_const_true(pn)) {
|
|
if (jump_if == true) {
|
|
EMIT(jump, label);
|
|
}
|
|
return;
|
|
} else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_or_test) {
|
|
if (jump_if == false) {
|
|
int label2 = comp_next_label(comp);
|
|
for (int i = 0; i < n - 1; i++) {
|
|
c_if_cond(comp, pns->nodes[i], true, label2);
|
|
}
|
|
c_if_cond(comp, pns->nodes[n - 1], false, label);
|
|
EMIT(label_assign, label2);
|
|
} else {
|
|
for (int i = 0; i < n; i++) {
|
|
c_if_cond(comp, pns->nodes[i], true, label);
|
|
}
|
|
}
|
|
return;
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_and_test) {
|
|
if (jump_if == false) {
|
|
for (int i = 0; i < n; i++) {
|
|
c_if_cond(comp, pns->nodes[i], false, label);
|
|
}
|
|
} else {
|
|
int label2 = comp_next_label(comp);
|
|
for (int i = 0; i < n - 1; i++) {
|
|
c_if_cond(comp, pns->nodes[i], false, label2);
|
|
}
|
|
c_if_cond(comp, pns->nodes[n - 1], true, label);
|
|
EMIT(label_assign, label2);
|
|
}
|
|
return;
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_not_test_2) {
|
|
c_if_cond(comp, pns->nodes[0], !jump_if, label);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// nothing special, fall back to default compiling for node and jump
|
|
compile_node(comp, pn);
|
|
if (jump_if == false) {
|
|
EMIT(pop_jump_if_false, label);
|
|
} else {
|
|
EMIT(pop_jump_if_true, label);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
typedef enum { ASSIGN_STORE, ASSIGN_AUG_LOAD, ASSIGN_AUG_STORE } assign_kind_t;
|
|
void c_assign(compiler_t *comp, mp_parse_node_t pn, assign_kind_t kind);
|
|
|
|
void c_assign_power(compiler_t *comp, mp_parse_node_struct_t *pns, assign_kind_t assign_kind) {
|
|
if (assign_kind != ASSIGN_AUG_STORE) {
|
|
compile_node(comp, pns->nodes[0]);
|
|
}
|
|
|
|
if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
|
|
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_power_trailers) {
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1);
|
|
if (assign_kind != ASSIGN_AUG_STORE) {
|
|
for (int i = 0; i < n - 1; i++) {
|
|
compile_node(comp, pns1->nodes[i]);
|
|
}
|
|
}
|
|
assert(MP_PARSE_NODE_IS_STRUCT(pns1->nodes[n - 1]));
|
|
pns1 = (mp_parse_node_struct_t*)pns1->nodes[n - 1];
|
|
}
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_paren) {
|
|
printf("SyntaxError: can't assign to function call\n");
|
|
return;
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_bracket) {
|
|
if (assign_kind == ASSIGN_AUG_STORE) {
|
|
EMIT(rot_three);
|
|
EMIT(store_subscr);
|
|
} else {
|
|
compile_node(comp, pns1->nodes[0]);
|
|
if (assign_kind == ASSIGN_AUG_LOAD) {
|
|
EMIT(dup_top_two);
|
|
EMIT(binary_op, RT_BINARY_OP_SUBSCR);
|
|
} else {
|
|
EMIT(store_subscr);
|
|
}
|
|
}
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) {
|
|
assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0]));
|
|
if (assign_kind == ASSIGN_AUG_LOAD) {
|
|
EMIT(dup_top);
|
|
EMIT(load_attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
|
|
} else {
|
|
if (assign_kind == ASSIGN_AUG_STORE) {
|
|
EMIT(rot_two);
|
|
}
|
|
EMIT(store_attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
|
|
}
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
|
|
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
|
|
// SyntaxError, cannot assign
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void c_assign_tuple(compiler_t *comp, int n, mp_parse_node_t *nodes) {
|
|
assert(n >= 0);
|
|
int have_star_index = -1;
|
|
for (int i = 0; i < n; i++) {
|
|
if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes[i], PN_star_expr)) {
|
|
if (have_star_index < 0) {
|
|
EMIT(unpack_ex, i, n - i - 1);
|
|
have_star_index = i;
|
|
} else {
|
|
printf("SyntaxError: two starred expressions in assignment\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (have_star_index < 0) {
|
|
EMIT(unpack_sequence, n);
|
|
}
|
|
for (int i = 0; i < n; i++) {
|
|
if (i == have_star_index) {
|
|
c_assign(comp, ((mp_parse_node_struct_t*)nodes[i])->nodes[0], ASSIGN_STORE);
|
|
} else {
|
|
c_assign(comp, nodes[i], ASSIGN_STORE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// assigns top of stack to pn
|
|
void c_assign(compiler_t *comp, mp_parse_node_t pn, assign_kind_t assign_kind) {
|
|
tail_recursion:
|
|
if (MP_PARSE_NODE_IS_NULL(pn)) {
|
|
assert(0);
|
|
} else if (MP_PARSE_NODE_IS_LEAF(pn)) {
|
|
if (MP_PARSE_NODE_IS_ID(pn)) {
|
|
int arg = MP_PARSE_NODE_LEAF_ARG(pn);
|
|
switch (assign_kind) {
|
|
case ASSIGN_STORE:
|
|
case ASSIGN_AUG_STORE:
|
|
EMIT(store_id, arg);
|
|
break;
|
|
case ASSIGN_AUG_LOAD:
|
|
EMIT(load_id, arg);
|
|
break;
|
|
}
|
|
} else {
|
|
printf("SyntaxError: can't assign to literal\n");
|
|
return;
|
|
}
|
|
} else {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
switch (MP_PARSE_NODE_STRUCT_KIND(pns)) {
|
|
case PN_power:
|
|
// lhs is an index or attribute
|
|
c_assign_power(comp, pns, assign_kind);
|
|
break;
|
|
|
|
case PN_testlist_star_expr:
|
|
case PN_exprlist:
|
|
// lhs is a tuple
|
|
if (assign_kind != ASSIGN_STORE) {
|
|
goto bad_aug;
|
|
}
|
|
c_assign_tuple(comp, MP_PARSE_NODE_STRUCT_NUM_NODES(pns), pns->nodes);
|
|
break;
|
|
|
|
case PN_atom_paren:
|
|
// lhs is something in parenthesis
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
|
|
// empty tuple
|
|
printf("SyntaxError: can't assign to ()\n");
|
|
return;
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
|
|
pns = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
goto testlist_comp;
|
|
} else {
|
|
// parenthesis around 1 item, is just that item
|
|
pn = pns->nodes[0];
|
|
goto tail_recursion;
|
|
}
|
|
break;
|
|
|
|
case PN_atom_bracket:
|
|
// lhs is something in brackets
|
|
if (assign_kind != ASSIGN_STORE) {
|
|
goto bad_aug;
|
|
}
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
|
|
// empty list, assignment allowed
|
|
c_assign_tuple(comp, 0, NULL);
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
|
|
pns = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
goto testlist_comp;
|
|
} else {
|
|
// brackets around 1 item
|
|
c_assign_tuple(comp, 1, &pns->nodes[0]);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
printf("unknown assign, %u\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns));
|
|
assert(0);
|
|
}
|
|
return;
|
|
|
|
testlist_comp:
|
|
// lhs is a sequence
|
|
if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
|
|
mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3b) {
|
|
// sequence of one item, with trailing comma
|
|
assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[0]));
|
|
c_assign_tuple(comp, 1, &pns->nodes[0]);
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3c) {
|
|
// sequence of many items
|
|
// TODO call c_assign_tuple instead
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns2);
|
|
EMIT(unpack_sequence, 1 + n);
|
|
c_assign(comp, pns->nodes[0], ASSIGN_STORE);
|
|
for (int i = 0; i < n; i++) {
|
|
c_assign(comp, pns2->nodes[i], ASSIGN_STORE);
|
|
}
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_comp_for) {
|
|
// TODO not implemented
|
|
assert(0);
|
|
} else {
|
|
// sequence with 2 items
|
|
goto sequence_with_2_items;
|
|
}
|
|
} else {
|
|
// sequence with 2 items
|
|
sequence_with_2_items:
|
|
c_assign_tuple(comp, 2, pns->nodes);
|
|
}
|
|
return;
|
|
}
|
|
return;
|
|
|
|
bad_aug:
|
|
printf("SyntaxError: illegal expression for augmented assignment\n");
|
|
}
|
|
|
|
// stuff for lambda and comprehensions and generators
|
|
void close_over_variables_etc(compiler_t *comp, scope_t *this_scope, int n_dict_params, int n_default_params) {
|
|
// make closed over variables, if any
|
|
// ensure they are closed over in the order defined in the outer scope (mainly to agree with CPython)
|
|
int nfree = 0;
|
|
if (comp->scope_cur->kind != SCOPE_MODULE) {
|
|
for (int i = 0; i < comp->scope_cur->id_info_len; i++) {
|
|
id_info_t *id = &comp->scope_cur->id_info[i];
|
|
if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) {
|
|
for (int j = 0; j < this_scope->id_info_len; j++) {
|
|
id_info_t *id2 = &this_scope->id_info[j];
|
|
if (id2->kind == ID_INFO_KIND_FREE && id->qstr == id2->qstr) {
|
|
#if MICROPY_EMIT_CPYTHON
|
|
EMIT(load_closure, id->qstr, id->local_num);
|
|
#else
|
|
// in Micro Python we load closures using LOAD_FAST
|
|
EMIT(load_fast, id->qstr, id->local_num);
|
|
#endif
|
|
nfree += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nfree > 0) {
|
|
EMIT(build_tuple, nfree);
|
|
}
|
|
|
|
// make the function/closure
|
|
if (nfree == 0) {
|
|
EMIT(make_function, this_scope, n_dict_params, n_default_params);
|
|
} else {
|
|
EMIT(make_closure, this_scope, n_dict_params, n_default_params);
|
|
}
|
|
}
|
|
|
|
void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) {
|
|
if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_name)) {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
|
|
// this parameter has a default value
|
|
// in CPython, None (and True, False?) as default parameters are loaded with LOAD_NAME; don't understandy why
|
|
if (comp->have_bare_star) {
|
|
comp->param_pass_num_dict_params += 1;
|
|
if (comp->param_pass == 1) {
|
|
EMIT(load_const_id, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
|
|
compile_node(comp, pns->nodes[2]);
|
|
}
|
|
} else {
|
|
comp->param_pass_num_default_params += 1;
|
|
if (comp->param_pass == 2) {
|
|
compile_node(comp, pns->nodes[2]);
|
|
}
|
|
}
|
|
}
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_typedargslist_star)) {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
|
|
// bare star
|
|
comp->have_bare_star = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// leaves function object on stack
|
|
// returns function name
|
|
qstr compile_funcdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint emit_options) {
|
|
if (comp->pass == PASS_1) {
|
|
// create a new scope for this function
|
|
scope_t *s = scope_new_and_link(comp, SCOPE_FUNCTION, (mp_parse_node_t)pns, emit_options);
|
|
// store the function scope so the compiling function can use it at each pass
|
|
pns->nodes[4] = (mp_parse_node_t)s;
|
|
}
|
|
|
|
// save variables (probably don't need to do this, since we can't have nested definitions..?)
|
|
bool old_have_bare_star = comp->have_bare_star;
|
|
int old_param_pass = comp->param_pass;
|
|
int old_param_pass_num_dict_params = comp->param_pass_num_dict_params;
|
|
int old_param_pass_num_default_params = comp->param_pass_num_default_params;
|
|
|
|
// compile default parameters
|
|
comp->have_bare_star = false;
|
|
comp->param_pass = 1; // pass 1 does any default parameters after bare star
|
|
comp->param_pass_num_dict_params = 0;
|
|
comp->param_pass_num_default_params = 0;
|
|
apply_to_single_or_list(comp, pns->nodes[1], PN_typedargslist, compile_funcdef_param);
|
|
comp->have_bare_star = false;
|
|
comp->param_pass = 2; // pass 2 does any default parameters before bare star
|
|
comp->param_pass_num_dict_params = 0;
|
|
comp->param_pass_num_default_params = 0;
|
|
apply_to_single_or_list(comp, pns->nodes[1], PN_typedargslist, compile_funcdef_param);
|
|
|
|
// get the scope for this function
|
|
scope_t *fscope = (scope_t*)pns->nodes[4];
|
|
|
|
// make the function
|
|
close_over_variables_etc(comp, fscope, comp->param_pass_num_dict_params, comp->param_pass_num_default_params);
|
|
|
|
// restore variables
|
|
comp->have_bare_star = old_have_bare_star;
|
|
comp->param_pass = old_param_pass;
|
|
comp->param_pass_num_dict_params = old_param_pass_num_dict_params;
|
|
comp->param_pass_num_default_params = old_param_pass_num_default_params;
|
|
|
|
// return its name (the 'f' in "def f(...):")
|
|
return fscope->simple_name;
|
|
}
|
|
|
|
// leaves class object on stack
|
|
// returns class name
|
|
qstr compile_classdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint emit_options) {
|
|
if (comp->pass == PASS_1) {
|
|
// create a new scope for this class
|
|
scope_t *s = scope_new_and_link(comp, SCOPE_CLASS, (mp_parse_node_t)pns, emit_options);
|
|
// store the class scope so the compiling function can use it at each pass
|
|
pns->nodes[3] = (mp_parse_node_t)s;
|
|
}
|
|
|
|
EMIT(load_build_class);
|
|
|
|
// scope for this class
|
|
scope_t *cscope = (scope_t*)pns->nodes[3];
|
|
|
|
// compile the class
|
|
close_over_variables_etc(comp, cscope, 0, 0);
|
|
|
|
// get its name
|
|
EMIT(load_const_id, cscope->simple_name);
|
|
|
|
// nodes[1] has parent classes, if any
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
|
|
// no parent classes
|
|
EMIT(call_function, 2, 0, false, false);
|
|
} else {
|
|
// have a parent class or classes
|
|
// TODO what if we have, eg, *a or **a in the parent list?
|
|
compile_node(comp, pns->nodes[1]);
|
|
EMIT(call_function, 2 + list_len(pns->nodes[1], PN_arglist), 0, false, false);
|
|
}
|
|
|
|
// return its name (the 'C' in class C(...):")
|
|
return cscope->simple_name;
|
|
}
|
|
|
|
// returns true if it was a built-in decorator (even if the built-in had an error)
|
|
static bool compile_built_in_decorator(compiler_t *comp, int name_len, mp_parse_node_t *name_nodes, uint *emit_options) {
|
|
if (MP_PARSE_NODE_LEAF_ARG(name_nodes[0]) != MP_QSTR_micropython) {
|
|
return false;
|
|
}
|
|
|
|
if (name_len != 2) {
|
|
printf("SyntaxError: invalid micropython decorator\n");
|
|
return true;
|
|
}
|
|
|
|
qstr attr = MP_PARSE_NODE_LEAF_ARG(name_nodes[1]);
|
|
if (attr == MP_QSTR_byte_code) {
|
|
*emit_options = EMIT_OPT_BYTE_CODE;
|
|
#if MICROPY_EMIT_NATIVE
|
|
} else if (attr == MP_QSTR_native) {
|
|
*emit_options = EMIT_OPT_NATIVE_PYTHON;
|
|
} else if (attr == MP_QSTR_viper) {
|
|
*emit_options = EMIT_OPT_VIPER;
|
|
#endif
|
|
#if MICROPY_EMIT_INLINE_THUMB
|
|
} else if (attr == MP_QSTR_asm_thumb) {
|
|
*emit_options = EMIT_OPT_ASM_THUMB;
|
|
#endif
|
|
} else {
|
|
printf("SyntaxError: invalid micropython decorator '%s'\n", qstr_str(attr));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void compile_decorated(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
// get the list of decorators
|
|
mp_parse_node_t *nodes;
|
|
int n = list_get(&pns->nodes[0], PN_decorators, &nodes);
|
|
|
|
// inherit emit options for this function/class definition
|
|
uint emit_options = comp->scope_cur->emit_options;
|
|
|
|
// compile each decorator
|
|
int num_built_in_decorators = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT_KIND(nodes[i], PN_decorator)); // should be
|
|
mp_parse_node_struct_t *pns_decorator = (mp_parse_node_struct_t*)nodes[i];
|
|
|
|
// nodes[0] contains the decorator function, which is a dotted name
|
|
mp_parse_node_t *name_nodes;
|
|
int name_len = list_get(&pns_decorator->nodes[0], PN_dotted_name, &name_nodes);
|
|
|
|
// check for built-in decorators
|
|
if (compile_built_in_decorator(comp, name_len, name_nodes, &emit_options)) {
|
|
// this was a built-in
|
|
num_built_in_decorators += 1;
|
|
|
|
} else {
|
|
// not a built-in, compile normally
|
|
|
|
// compile the decorator function
|
|
compile_node(comp, name_nodes[0]);
|
|
for (int i = 1; i < name_len; i++) {
|
|
assert(MP_PARSE_NODE_IS_ID(name_nodes[i])); // should be
|
|
EMIT(load_attr, MP_PARSE_NODE_LEAF_ARG(name_nodes[i]));
|
|
}
|
|
|
|
// nodes[1] contains arguments to the decorator function, if any
|
|
if (!MP_PARSE_NODE_IS_NULL(pns_decorator->nodes[1])) {
|
|
// call the decorator function with the arguments in nodes[1]
|
|
compile_node(comp, pns_decorator->nodes[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// compile the body (funcdef or classdef) and get its name
|
|
mp_parse_node_struct_t *pns_body = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
qstr body_name = 0;
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns_body) == PN_funcdef) {
|
|
body_name = compile_funcdef_helper(comp, pns_body, emit_options);
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns_body) == PN_classdef) {
|
|
body_name = compile_classdef_helper(comp, pns_body, emit_options);
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
|
|
// call each decorator
|
|
for (int i = 0; i < n - num_built_in_decorators; i++) {
|
|
EMIT(call_function, 1, 0, false, false);
|
|
}
|
|
|
|
// store func/class object into name
|
|
EMIT(store_id, body_name);
|
|
}
|
|
|
|
void compile_funcdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
qstr fname = compile_funcdef_helper(comp, pns, comp->scope_cur->emit_options);
|
|
// store function object into function name
|
|
EMIT(store_id, fname);
|
|
}
|
|
|
|
void c_del_stmt(compiler_t *comp, mp_parse_node_t pn) {
|
|
if (MP_PARSE_NODE_IS_ID(pn)) {
|
|
EMIT(delete_id, MP_PARSE_NODE_LEAF_ARG(pn));
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_power)) {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
|
|
compile_node(comp, pns->nodes[0]); // base of the power node
|
|
|
|
if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
|
|
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_power_trailers) {
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1);
|
|
for (int i = 0; i < n - 1; i++) {
|
|
compile_node(comp, pns1->nodes[i]);
|
|
}
|
|
assert(MP_PARSE_NODE_IS_STRUCT(pns1->nodes[n - 1]));
|
|
pns1 = (mp_parse_node_struct_t*)pns1->nodes[n - 1];
|
|
}
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_paren) {
|
|
// SyntaxError: can't delete a function call
|
|
assert(0);
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_bracket) {
|
|
compile_node(comp, pns1->nodes[0]);
|
|
EMIT(delete_subscr);
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_trailer_period) {
|
|
assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0]));
|
|
EMIT(delete_attr, MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]));
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
|
|
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
|
|
// SyntaxError, cannot delete
|
|
assert(0);
|
|
}
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_paren)) {
|
|
pn = ((mp_parse_node_struct_t*)pn)->nodes[0];
|
|
if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_testlist_comp)) {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
// TODO perhaps factorise testlist_comp code with other uses of PN_testlist_comp
|
|
|
|
if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
|
|
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_testlist_comp_3b) {
|
|
// sequence of one item, with trailing comma
|
|
assert(MP_PARSE_NODE_IS_NULL(pns1->nodes[0]));
|
|
c_del_stmt(comp, pns->nodes[0]);
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_testlist_comp_3c) {
|
|
// sequence of many items
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1);
|
|
c_del_stmt(comp, pns->nodes[0]);
|
|
for (int i = 0; i < n; i++) {
|
|
c_del_stmt(comp, pns1->nodes[i]);
|
|
}
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_comp_for) {
|
|
// TODO not implemented; can't del comprehension?
|
|
assert(0);
|
|
} else {
|
|
// sequence with 2 items
|
|
goto sequence_with_2_items;
|
|
}
|
|
} else {
|
|
// sequence with 2 items
|
|
sequence_with_2_items:
|
|
c_del_stmt(comp, pns->nodes[0]);
|
|
c_del_stmt(comp, pns->nodes[1]);
|
|
}
|
|
} else {
|
|
// tuple with 1 element
|
|
c_del_stmt(comp, pn);
|
|
}
|
|
} else {
|
|
// not implemented
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void compile_del_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
apply_to_single_or_list(comp, pns->nodes[0], PN_exprlist, c_del_stmt);
|
|
}
|
|
|
|
void compile_break_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (comp->break_label == 0) {
|
|
printf("ERROR: cannot break from here\n");
|
|
}
|
|
EMIT(break_loop, comp->break_label);
|
|
}
|
|
|
|
void compile_continue_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (comp->continue_label == 0) {
|
|
printf("ERROR: cannot continue from here\n");
|
|
}
|
|
if (comp->except_nest_level > 0) {
|
|
EMIT(continue_loop, comp->continue_label);
|
|
} else {
|
|
EMIT(jump, comp->continue_label);
|
|
}
|
|
}
|
|
|
|
void compile_return_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (comp->scope_cur->kind != SCOPE_FUNCTION) {
|
|
printf("SyntaxError: 'return' outside function\n");
|
|
comp->had_error = true;
|
|
return;
|
|
}
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
|
|
// no argument to 'return', so return None
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_test_if_expr)) {
|
|
// special case when returning an if-expression; to match CPython optimisation
|
|
mp_parse_node_struct_t *pns_test_if_expr = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
mp_parse_node_struct_t *pns_test_if_else = (mp_parse_node_struct_t*)pns_test_if_expr->nodes[1];
|
|
|
|
int l_fail = comp_next_label(comp);
|
|
c_if_cond(comp, pns_test_if_else->nodes[0], false, l_fail); // condition
|
|
compile_node(comp, pns_test_if_expr->nodes[0]); // success value
|
|
EMIT(return_value);
|
|
EMIT(label_assign, l_fail);
|
|
compile_node(comp, pns_test_if_else->nodes[1]); // failure value
|
|
} else {
|
|
compile_node(comp, pns->nodes[0]);
|
|
}
|
|
EMIT(return_value);
|
|
}
|
|
|
|
void compile_yield_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(pop_top);
|
|
}
|
|
|
|
void compile_raise_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
|
|
// raise
|
|
EMIT(raise_varargs, 0);
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_raise_stmt_arg)) {
|
|
// raise x from y
|
|
pns = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
compile_node(comp, pns->nodes[0]);
|
|
compile_node(comp, pns->nodes[1]);
|
|
EMIT(raise_varargs, 2);
|
|
} else {
|
|
// raise x
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(raise_varargs, 1);
|
|
}
|
|
}
|
|
|
|
// q1 holds the base, q2 the full name
|
|
// eg a -> q1=q2=a
|
|
// a.b.c -> q1=a, q2=a.b.c
|
|
void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q1, qstr *q2) {
|
|
bool is_as = false;
|
|
if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_dotted_as_name)) {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
// a name of the form x as y; unwrap it
|
|
*q1 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[1]);
|
|
pn = pns->nodes[0];
|
|
is_as = true;
|
|
}
|
|
if (MP_PARSE_NODE_IS_ID(pn)) {
|
|
// just a simple name
|
|
*q2 = MP_PARSE_NODE_LEAF_ARG(pn);
|
|
if (!is_as) {
|
|
*q1 = *q2;
|
|
}
|
|
EMIT(import_name, *q2);
|
|
} else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dotted_name) {
|
|
// a name of the form a.b.c
|
|
if (!is_as) {
|
|
*q1 = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
|
|
}
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
int len = n - 1;
|
|
for (int i = 0; i < n; i++) {
|
|
len += strlen(qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])));
|
|
}
|
|
char *str = m_new(char, len + 1);
|
|
char *str_dest = str;
|
|
str[0] = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
if (i > 0) {
|
|
*str_dest++ = '.';
|
|
}
|
|
const char *str_src = qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
|
|
size_t str_src_len = strlen(str_src);
|
|
memcpy(str_dest, str_src, str_src_len);
|
|
str_dest += str_src_len;
|
|
}
|
|
*str_dest = '\0';
|
|
*q2 = qstr_from_str_take(str, len + 1);
|
|
EMIT(import_name, *q2);
|
|
if (is_as) {
|
|
for (int i = 1; i < n; i++) {
|
|
EMIT(load_attr, MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
|
|
}
|
|
}
|
|
} else {
|
|
// TODO not implemented
|
|
assert(0);
|
|
}
|
|
} else {
|
|
// TODO not implemented
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void compile_dotted_as_name(compiler_t *comp, mp_parse_node_t pn) {
|
|
EMIT(load_const_small_int, 0); // ??
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
qstr q1, q2;
|
|
do_import_name(comp, pn, &q1, &q2);
|
|
EMIT(store_id, q1);
|
|
}
|
|
|
|
void compile_import_name(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
apply_to_single_or_list(comp, pns->nodes[0], PN_dotted_as_names, compile_dotted_as_name);
|
|
}
|
|
|
|
void compile_import_from(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_STAR)) {
|
|
EMIT(load_const_small_int, 0); // level 0 for __import__
|
|
|
|
// build the "fromlist" tuple
|
|
#if MICROPY_EMIT_CPYTHON
|
|
EMIT(load_const_verbatim_str, "('*',)");
|
|
#else
|
|
EMIT(load_const_str, qstr_from_str_static("*"), false);
|
|
EMIT(build_tuple, 1);
|
|
#endif
|
|
|
|
// do the import
|
|
qstr dummy_q, id1;
|
|
do_import_name(comp, pns->nodes[0], &dummy_q, &id1);
|
|
EMIT(import_star);
|
|
|
|
} else {
|
|
EMIT(load_const_small_int, 0); // level 0 for __import__
|
|
|
|
// build the "fromlist" tuple
|
|
mp_parse_node_t *pn_nodes;
|
|
int n = list_get(&pns->nodes[1], PN_import_as_names, &pn_nodes);
|
|
#if MICROPY_EMIT_CPYTHON
|
|
{
|
|
vstr_t *vstr = vstr_new();
|
|
vstr_printf(vstr, "(");
|
|
for (int i = 0; i < n; i++) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
|
|
mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pn_nodes[i];
|
|
qstr id2 = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
|
|
if (i > 0) {
|
|
vstr_printf(vstr, ", ");
|
|
}
|
|
vstr_printf(vstr, "'");
|
|
vstr_printf(vstr, qstr_str(id2));
|
|
vstr_printf(vstr, "'");
|
|
}
|
|
if (n == 1) {
|
|
vstr_printf(vstr, ",");
|
|
}
|
|
vstr_printf(vstr, ")");
|
|
EMIT(load_const_verbatim_str, vstr_str(vstr));
|
|
vstr_free(vstr);
|
|
}
|
|
#else
|
|
for (int i = 0; i < n; i++) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
|
|
mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pn_nodes[i];
|
|
qstr id2 = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
|
|
EMIT(load_const_str, id2, false);
|
|
}
|
|
EMIT(build_tuple, n);
|
|
#endif
|
|
|
|
// do the import
|
|
qstr dummy_q, id1;
|
|
do_import_name(comp, pns->nodes[0], &dummy_q, &id1);
|
|
for (int i = 0; i < n; i++) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_nodes[i], PN_import_as_name));
|
|
mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pn_nodes[i];
|
|
qstr id2 = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[0]); // should be id
|
|
EMIT(import_from, id2);
|
|
if (MP_PARSE_NODE_IS_NULL(pns3->nodes[1])) {
|
|
EMIT(store_id, id2);
|
|
} else {
|
|
EMIT(store_id, MP_PARSE_NODE_LEAF_ARG(pns3->nodes[1]));
|
|
}
|
|
}
|
|
EMIT(pop_top);
|
|
}
|
|
}
|
|
|
|
void compile_global_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (comp->pass == PASS_1) {
|
|
if (MP_PARSE_NODE_IS_LEAF(pns->nodes[0])) {
|
|
scope_declare_global(comp->scope_cur, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
|
|
} else {
|
|
pns = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
for (int i = 0; i < num_nodes; i++) {
|
|
scope_declare_global(comp->scope_cur, MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void compile_nonlocal_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (comp->pass == PASS_1) {
|
|
if (MP_PARSE_NODE_IS_LEAF(pns->nodes[0])) {
|
|
scope_declare_nonlocal(comp->scope_cur, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
|
|
} else {
|
|
pns = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
for (int i = 0; i < num_nodes; i++) {
|
|
scope_declare_nonlocal(comp->scope_cur, MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void compile_assert_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
int l_end = comp_next_label(comp);
|
|
c_if_cond(comp, pns->nodes[0], true, l_end);
|
|
EMIT(load_id, MP_QSTR_assertion_error);
|
|
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
|
|
// assertion message
|
|
compile_node(comp, pns->nodes[1]);
|
|
EMIT(call_function, 1, 0, false, false);
|
|
}
|
|
EMIT(raise_varargs, 1);
|
|
EMIT(label_assign, l_end);
|
|
}
|
|
|
|
void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
// TODO proper and/or short circuiting
|
|
|
|
int l_end = comp_next_label(comp);
|
|
|
|
int l_fail = comp_next_label(comp);
|
|
c_if_cond(comp, pns->nodes[0], false, l_fail); // if condition
|
|
|
|
compile_node(comp, pns->nodes[1]); // if block
|
|
//if (!(MP_PARSE_NODE_IS_NULL(pns->nodes[2]) && MP_PARSE_NODE_IS_NULL(pns->nodes[3]))) { // optimisation; doesn't align with CPython
|
|
// jump over elif/else blocks if they exist
|
|
if (!EMIT(last_emit_was_return_value)) { // simple optimisation to align with CPython
|
|
EMIT(jump, l_end);
|
|
}
|
|
//}
|
|
EMIT(label_assign, l_fail);
|
|
|
|
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
|
|
// compile elif blocks
|
|
|
|
mp_parse_node_struct_t *pns_elif = (mp_parse_node_struct_t*)pns->nodes[2];
|
|
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns_elif) == PN_if_stmt_elif_list) {
|
|
// multiple elif blocks
|
|
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns_elif);
|
|
for (int i = 0; i < n; i++) {
|
|
mp_parse_node_struct_t *pns_elif2 = (mp_parse_node_struct_t*)pns_elif->nodes[i];
|
|
l_fail = comp_next_label(comp);
|
|
c_if_cond(comp, pns_elif2->nodes[0], false, l_fail); // elif condition
|
|
|
|
compile_node(comp, pns_elif2->nodes[1]); // elif block
|
|
if (!EMIT(last_emit_was_return_value)) { // simple optimisation to align with CPython
|
|
EMIT(jump, l_end);
|
|
}
|
|
EMIT(label_assign, l_fail);
|
|
}
|
|
|
|
} else {
|
|
// a single elif block
|
|
|
|
l_fail = comp_next_label(comp);
|
|
c_if_cond(comp, pns_elif->nodes[0], false, l_fail); // elif condition
|
|
|
|
compile_node(comp, pns_elif->nodes[1]); // elif block
|
|
if (!EMIT(last_emit_was_return_value)) { // simple optimisation to align with CPython
|
|
EMIT(jump, l_end);
|
|
}
|
|
EMIT(label_assign, l_fail);
|
|
}
|
|
}
|
|
|
|
// compile else block
|
|
compile_node(comp, pns->nodes[3]); // can be null
|
|
|
|
EMIT(label_assign, l_end);
|
|
}
|
|
|
|
void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
int old_break_label = comp->break_label;
|
|
int old_continue_label = comp->continue_label;
|
|
|
|
int break_label = comp_next_label(comp);
|
|
int continue_label = comp_next_label(comp);
|
|
|
|
comp->break_label = break_label;
|
|
comp->continue_label = continue_label;
|
|
|
|
// compared to CPython, we have an optimised version of while loops
|
|
#if MICROPY_EMIT_CPYTHON
|
|
int done_label = comp_next_label(comp);
|
|
EMIT(setup_loop, break_label);
|
|
EMIT(label_assign, continue_label);
|
|
c_if_cond(comp, pns->nodes[0], false, done_label); // condition
|
|
compile_node(comp, pns->nodes[1]); // body
|
|
if (!EMIT(last_emit_was_return_value)) {
|
|
EMIT(jump, continue_label);
|
|
}
|
|
EMIT(label_assign, done_label);
|
|
// CPython does not emit POP_BLOCK if the condition was a constant; don't undertand why
|
|
// this is a small hack to agree with CPython
|
|
if (!node_is_const_true(pns->nodes[0])) {
|
|
EMIT(pop_block);
|
|
}
|
|
#else
|
|
int top_label = comp_next_label(comp);
|
|
EMIT(jump, continue_label);
|
|
EMIT(label_assign, top_label);
|
|
compile_node(comp, pns->nodes[1]); // body
|
|
EMIT(label_assign, continue_label);
|
|
c_if_cond(comp, pns->nodes[0], true, top_label); // condition
|
|
#endif
|
|
|
|
// break/continue apply to outer loop (if any) in the else block
|
|
comp->break_label = old_break_label;
|
|
comp->continue_label = old_continue_label;
|
|
|
|
compile_node(comp, pns->nodes[2]); // else
|
|
|
|
EMIT(label_assign, break_label);
|
|
}
|
|
|
|
// TODO preload end and step onto stack if they are not constants
|
|
// TODO check if step is negative and do opposite test
|
|
void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t pn_var, mp_parse_node_t pn_start, mp_parse_node_t pn_end, mp_parse_node_t pn_step, mp_parse_node_t pn_body, mp_parse_node_t pn_else) {
|
|
int old_break_label = comp->break_label;
|
|
int old_continue_label = comp->continue_label;
|
|
|
|
int break_label = comp_next_label(comp);
|
|
int continue_label = comp_next_label(comp);
|
|
|
|
comp->break_label = break_label;
|
|
comp->continue_label = continue_label;
|
|
|
|
int top_label = comp_next_label(comp);
|
|
|
|
// compile: var = start
|
|
compile_node(comp, pn_start);
|
|
c_assign(comp, pn_var, ASSIGN_STORE);
|
|
|
|
EMIT(jump, continue_label);
|
|
EMIT(label_assign, top_label);
|
|
|
|
// compile body
|
|
compile_node(comp, pn_body);
|
|
|
|
// compile: var += step
|
|
c_assign(comp, pn_var, ASSIGN_AUG_LOAD);
|
|
compile_node(comp, pn_step);
|
|
EMIT(binary_op, RT_BINARY_OP_INPLACE_ADD);
|
|
c_assign(comp, pn_var, ASSIGN_AUG_STORE);
|
|
|
|
EMIT(label_assign, continue_label);
|
|
|
|
// compile: if var < end: goto top
|
|
compile_node(comp, pn_var);
|
|
compile_node(comp, pn_end);
|
|
EMIT(compare_op, RT_COMPARE_OP_LESS);
|
|
EMIT(pop_jump_if_true, top_label);
|
|
|
|
// break/continue apply to outer loop (if any) in the else block
|
|
comp->break_label = old_break_label;
|
|
comp->continue_label = old_continue_label;
|
|
|
|
compile_node(comp, pn_else);
|
|
|
|
EMIT(label_assign, break_label);
|
|
}
|
|
|
|
void compile_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
#if !MICROPY_EMIT_CPYTHON
|
|
// this bit optimises: for <x> in range(...), turning it into an explicitly incremented variable
|
|
// this is actually slower, but uses no heap memory
|
|
// for viper it will be much, much faster
|
|
if (/*comp->scope_cur->emit_options == EMIT_OPT_VIPER &&*/ MP_PARSE_NODE_IS_ID(pns->nodes[0]) && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_power)) {
|
|
mp_parse_node_struct_t *pns_it = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
if (MP_PARSE_NODE_IS_ID(pns_it->nodes[0]) && MP_PARSE_NODE_LEAF_ARG(pns_it->nodes[0]) == MP_QSTR_range && MP_PARSE_NODE_IS_STRUCT_KIND(pns_it->nodes[1], PN_trailer_paren) && MP_PARSE_NODE_IS_NULL(pns_it->nodes[2])) {
|
|
mp_parse_node_t pn_range_args = ((mp_parse_node_struct_t*)pns_it->nodes[1])->nodes[0];
|
|
mp_parse_node_t *args;
|
|
int n_args = list_get(&pn_range_args, PN_arglist, &args);
|
|
if (1 <= n_args && n_args <= 3) {
|
|
mp_parse_node_t pn_range_start;
|
|
mp_parse_node_t pn_range_end;
|
|
mp_parse_node_t pn_range_step;
|
|
if (n_args == 1) {
|
|
pn_range_start = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, 0);
|
|
pn_range_end = args[0];
|
|
pn_range_step = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, 1);
|
|
} else if (n_args == 2) {
|
|
pn_range_start = args[0];
|
|
pn_range_end = args[1];
|
|
pn_range_step = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, 1);
|
|
} else {
|
|
pn_range_start = args[0];
|
|
pn_range_end = args[1];
|
|
pn_range_step = args[2];
|
|
}
|
|
compile_for_stmt_optimised_range(comp, pns->nodes[0], pn_range_start, pn_range_end, pn_range_step, pns->nodes[2], pns->nodes[3]);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int old_break_label = comp->break_label;
|
|
int old_continue_label = comp->continue_label;
|
|
|
|
int for_label = comp_next_label(comp);
|
|
int pop_label = comp_next_label(comp);
|
|
int end_label = comp_next_label(comp);
|
|
|
|
int break_label = comp_next_label(comp);
|
|
|
|
comp->continue_label = for_label;
|
|
comp->break_label = break_label;
|
|
|
|
// I don't think our implementation needs SETUP_LOOP/POP_BLOCK for for-statements
|
|
#if MICROPY_EMIT_CPYTHON
|
|
EMIT(setup_loop, end_label);
|
|
#endif
|
|
|
|
compile_node(comp, pns->nodes[1]); // iterator
|
|
EMIT(get_iter);
|
|
EMIT(label_assign, for_label);
|
|
EMIT(for_iter, pop_label);
|
|
c_assign(comp, pns->nodes[0], ASSIGN_STORE); // variable
|
|
compile_node(comp, pns->nodes[2]); // body
|
|
if (!EMIT(last_emit_was_return_value)) {
|
|
EMIT(jump, for_label);
|
|
}
|
|
EMIT(label_assign, pop_label);
|
|
EMIT(for_iter_end);
|
|
|
|
// break/continue apply to outer loop (if any) in the else block
|
|
comp->break_label = old_break_label;
|
|
comp->continue_label = old_continue_label;
|
|
|
|
#if MICROPY_EMIT_CPYTHON
|
|
EMIT(pop_block);
|
|
#endif
|
|
|
|
compile_node(comp, pns->nodes[3]); // else (not tested)
|
|
|
|
EMIT(label_assign, break_label);
|
|
EMIT(label_assign, end_label);
|
|
}
|
|
|
|
void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_except, mp_parse_node_t *pn_excepts, mp_parse_node_t pn_else) {
|
|
// this function is a bit of a hack at the moment
|
|
// don't understand how the stack works with exceptions, so we force it to return to the correct value
|
|
|
|
// setup code
|
|
int stack_size = EMIT(get_stack_size);
|
|
int l1 = comp_next_label(comp);
|
|
int success_label = comp_next_label(comp);
|
|
comp->except_nest_level += 1; // for correct handling of continue
|
|
EMIT(setup_except, l1);
|
|
compile_node(comp, pn_body); // body
|
|
EMIT(pop_block);
|
|
EMIT(jump, success_label);
|
|
EMIT(label_assign, l1);
|
|
int l2 = comp_next_label(comp);
|
|
|
|
for (int i = 0; i < n_except; i++) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_excepts[i], PN_try_stmt_except)); // should be
|
|
mp_parse_node_struct_t *pns_except = (mp_parse_node_struct_t*)pn_excepts[i];
|
|
|
|
qstr qstr_exception_local = 0;
|
|
int end_finally_label = comp_next_label(comp);
|
|
|
|
if (MP_PARSE_NODE_IS_NULL(pns_except->nodes[0])) {
|
|
// this is a catch all exception handler
|
|
if (i + 1 != n_except) {
|
|
printf("SyntaxError: default 'except:' must be last\n");
|
|
return;
|
|
}
|
|
} else {
|
|
// this exception handler requires a match to a certain type of exception
|
|
mp_parse_node_t pns_exception_expr = pns_except->nodes[0];
|
|
if (MP_PARSE_NODE_IS_STRUCT(pns_exception_expr)) {
|
|
mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pns_exception_expr;
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_try_stmt_as_name) {
|
|
// handler binds the exception to a local
|
|
pns_exception_expr = pns3->nodes[0];
|
|
qstr_exception_local = MP_PARSE_NODE_LEAF_ARG(pns3->nodes[1]);
|
|
}
|
|
}
|
|
EMIT(dup_top);
|
|
compile_node(comp, pns_exception_expr);
|
|
EMIT(compare_op, RT_COMPARE_OP_EXCEPTION_MATCH);
|
|
EMIT(pop_jump_if_false, end_finally_label);
|
|
}
|
|
|
|
EMIT(pop_top);
|
|
|
|
if (qstr_exception_local == 0) {
|
|
EMIT(pop_top);
|
|
} else {
|
|
EMIT(store_id, qstr_exception_local);
|
|
}
|
|
|
|
EMIT(pop_top);
|
|
|
|
int l3 = 0;
|
|
if (qstr_exception_local != 0) {
|
|
l3 = comp_next_label(comp);
|
|
EMIT(setup_finally, l3);
|
|
}
|
|
compile_node(comp, pns_except->nodes[1]);
|
|
if (qstr_exception_local != 0) {
|
|
EMIT(pop_block);
|
|
}
|
|
EMIT(pop_except);
|
|
if (qstr_exception_local != 0) {
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
EMIT(label_assign, l3);
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
EMIT(store_id, qstr_exception_local);
|
|
EMIT(delete_id, qstr_exception_local);
|
|
EMIT(end_finally);
|
|
}
|
|
EMIT(jump, l2);
|
|
EMIT(label_assign, end_finally_label);
|
|
}
|
|
|
|
EMIT(end_finally);
|
|
EMIT(label_assign, success_label);
|
|
comp->except_nest_level -= 1;
|
|
compile_node(comp, pn_else); // else block, can be null
|
|
EMIT(label_assign, l2);
|
|
EMIT(set_stack_size, stack_size);
|
|
}
|
|
|
|
void compile_try_finally(compiler_t *comp, mp_parse_node_t pn_body, int n_except, mp_parse_node_t *pn_except, mp_parse_node_t pn_else, mp_parse_node_t pn_finally) {
|
|
// don't understand how the stack works with exceptions, so we force it to return to the correct value
|
|
int stack_size = EMIT(get_stack_size);
|
|
int l_finally_block = comp_next_label(comp);
|
|
EMIT(setup_finally, l_finally_block);
|
|
if (n_except == 0) {
|
|
assert(MP_PARSE_NODE_IS_NULL(pn_else));
|
|
compile_node(comp, pn_body);
|
|
} else {
|
|
compile_try_except(comp, pn_body, n_except, pn_except, pn_else);
|
|
}
|
|
EMIT(pop_block);
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
EMIT(label_assign, l_finally_block);
|
|
compile_node(comp, pn_finally);
|
|
EMIT(end_finally);
|
|
EMIT(set_stack_size, stack_size);
|
|
}
|
|
|
|
void compile_try_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
|
|
mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_try_stmt_finally) {
|
|
// just try-finally
|
|
compile_try_finally(comp, pns->nodes[0], 0, NULL, MP_PARSE_NODE_NULL, pns2->nodes[0]);
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_try_stmt_except_and_more) {
|
|
// try-except and possibly else and/or finally
|
|
mp_parse_node_t *pn_excepts;
|
|
int n_except = list_get(&pns2->nodes[0], PN_try_stmt_except_list, &pn_excepts);
|
|
if (MP_PARSE_NODE_IS_NULL(pns2->nodes[2])) {
|
|
// no finally
|
|
compile_try_except(comp, pns->nodes[0], n_except, pn_excepts, pns2->nodes[1]);
|
|
} else {
|
|
// have finally
|
|
compile_try_finally(comp, pns->nodes[0], n_except, pn_excepts, pns2->nodes[1], ((mp_parse_node_struct_t*)pns2->nodes[2])->nodes[0]);
|
|
}
|
|
} else {
|
|
// just try-except
|
|
mp_parse_node_t *pn_excepts;
|
|
int n_except = list_get(&pns->nodes[1], PN_try_stmt_except_list, &pn_excepts);
|
|
compile_try_except(comp, pns->nodes[0], n_except, pn_excepts, MP_PARSE_NODE_NULL);
|
|
}
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void compile_with_stmt_helper(compiler_t *comp, int n, mp_parse_node_t *nodes, mp_parse_node_t body) {
|
|
if (n == 0) {
|
|
// no more pre-bits, compile the body of the with
|
|
compile_node(comp, body);
|
|
} else {
|
|
int l_end = comp_next_label(comp);
|
|
if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes[0], PN_with_item)) {
|
|
// this pre-bit is of the form "a as b"
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)nodes[0];
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(setup_with, l_end);
|
|
c_assign(comp, pns->nodes[1], ASSIGN_STORE);
|
|
} else {
|
|
// this pre-bit is just an expression
|
|
compile_node(comp, nodes[0]);
|
|
EMIT(setup_with, l_end);
|
|
EMIT(pop_top);
|
|
}
|
|
// compile additional pre-bits and the body
|
|
compile_with_stmt_helper(comp, n - 1, nodes + 1, body);
|
|
// finish this with block
|
|
EMIT(pop_block);
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
EMIT(label_assign, l_end);
|
|
EMIT(with_cleanup);
|
|
EMIT(end_finally);
|
|
}
|
|
}
|
|
|
|
void compile_with_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
// get the nodes for the pre-bit of the with (the a as b, c as d, ... bit)
|
|
mp_parse_node_t *nodes;
|
|
int n = list_get(&pns->nodes[0], PN_with_stmt_list, &nodes);
|
|
assert(n > 0);
|
|
|
|
// compile in a nested fashion
|
|
compile_with_stmt_helper(comp, n, nodes, pns->nodes[1]);
|
|
}
|
|
|
|
void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
|
|
if (comp->is_repl && comp->scope_cur->kind == SCOPE_MODULE) {
|
|
// for REPL, evaluate then print the expression
|
|
EMIT(load_id, MP_QSTR___repl_print__);
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(call_function, 1, 0, false, false);
|
|
EMIT(pop_top);
|
|
|
|
} else {
|
|
// for non-REPL, evaluate then discard the expression
|
|
if (MP_PARSE_NODE_IS_LEAF(pns->nodes[0]) && !MP_PARSE_NODE_IS_ID(pns->nodes[0])) {
|
|
// do nothing with a lonely constant
|
|
} else {
|
|
compile_node(comp, pns->nodes[0]); // just an expression
|
|
EMIT(pop_top); // discard last result since this is a statement and leaves nothing on the stack
|
|
}
|
|
}
|
|
} else {
|
|
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
int kind = MP_PARSE_NODE_STRUCT_KIND(pns1);
|
|
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]));
|
|
// note that we don't really need to implement separate inplace ops, just normal binary ops will suffice
|
|
switch (MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0])) {
|
|
case MP_TOKEN_DEL_PIPE_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_OR); break;
|
|
case MP_TOKEN_DEL_CARET_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_XOR); break;
|
|
case MP_TOKEN_DEL_AMPERSAND_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_AND); break;
|
|
case MP_TOKEN_DEL_DBL_LESS_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_LSHIFT); break;
|
|
case MP_TOKEN_DEL_DBL_MORE_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_RSHIFT); break;
|
|
case MP_TOKEN_DEL_PLUS_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_ADD); break;
|
|
case MP_TOKEN_DEL_MINUS_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_SUBTRACT); break;
|
|
case MP_TOKEN_DEL_STAR_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_MULTIPLY); break;
|
|
case MP_TOKEN_DEL_DBL_SLASH_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_FLOOR_DIVIDE); break;
|
|
case MP_TOKEN_DEL_SLASH_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_TRUE_DIVIDE); break;
|
|
case MP_TOKEN_DEL_PERCENT_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_MODULO); break;
|
|
case MP_TOKEN_DEL_DBL_STAR_EQUAL: EMIT(binary_op, RT_BINARY_OP_INPLACE_POWER); break;
|
|
default: assert(0); // shouldn't happen
|
|
}
|
|
c_assign(comp, pns->nodes[0], ASSIGN_AUG_STORE); // lhs store for aug assign
|
|
} else if (kind == PN_expr_stmt_assign_list) {
|
|
int rhs = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1) - 1;
|
|
compile_node(comp, ((mp_parse_node_struct_t*)pns1->nodes[rhs])->nodes[0]); // rhs
|
|
// following CPython, we store left-most first
|
|
if (rhs > 0) {
|
|
EMIT(dup_top);
|
|
}
|
|
c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store
|
|
for (int i = 0; i < rhs; i++) {
|
|
if (i + 1 < rhs) {
|
|
EMIT(dup_top);
|
|
}
|
|
c_assign(comp, ((mp_parse_node_struct_t*)pns1->nodes[i])->nodes[0], ASSIGN_STORE); // middle store
|
|
}
|
|
} else if (kind == PN_expr_stmt_assign) {
|
|
if (MP_PARSE_NODE_IS_STRUCT_KIND(pns1->nodes[0], PN_testlist_star_expr)
|
|
&& MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
|
|
&& MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns1->nodes[0]) == 2
|
|
&& MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[0]) == 2) {
|
|
// optimisation for a, b = c, d; to match CPython's optimisation
|
|
mp_parse_node_struct_t* pns10 = (mp_parse_node_struct_t*)pns1->nodes[0];
|
|
mp_parse_node_struct_t* pns0 = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
compile_node(comp, pns10->nodes[0]); // rhs
|
|
compile_node(comp, pns10->nodes[1]); // rhs
|
|
EMIT(rot_two);
|
|
c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store
|
|
c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns1->nodes[0], PN_testlist_star_expr)
|
|
&& MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
|
|
&& MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns1->nodes[0]) == 3
|
|
&& MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[0]) == 3) {
|
|
// optimisation for a, b, c = d, e, f; to match CPython's optimisation
|
|
mp_parse_node_struct_t* pns10 = (mp_parse_node_struct_t*)pns1->nodes[0];
|
|
mp_parse_node_struct_t* pns0 = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
compile_node(comp, pns10->nodes[0]); // rhs
|
|
compile_node(comp, pns10->nodes[1]); // rhs
|
|
compile_node(comp, pns10->nodes[2]); // rhs
|
|
EMIT(rot_three);
|
|
EMIT(rot_two);
|
|
c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store
|
|
c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store
|
|
c_assign(comp, pns0->nodes[2], ASSIGN_STORE); // lhs store
|
|
} else {
|
|
compile_node(comp, pns1->nodes[0]); // rhs
|
|
c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store
|
|
}
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void c_binary_op(compiler_t *comp, mp_parse_node_struct_t *pns, rt_binary_op_t binary_op) {
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
compile_node(comp, pns->nodes[0]);
|
|
for (int i = 1; i < num_nodes; i += 1) {
|
|
compile_node(comp, pns->nodes[i]);
|
|
EMIT(binary_op, binary_op);
|
|
}
|
|
}
|
|
|
|
void compile_test_if_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_test_if_else));
|
|
mp_parse_node_struct_t *pns_test_if_else = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
|
|
int stack_size = EMIT(get_stack_size);
|
|
int l_fail = comp_next_label(comp);
|
|
int l_end = comp_next_label(comp);
|
|
c_if_cond(comp, pns_test_if_else->nodes[0], false, l_fail); // condition
|
|
compile_node(comp, pns->nodes[0]); // success value
|
|
EMIT(jump, l_end);
|
|
EMIT(label_assign, l_fail);
|
|
EMIT(set_stack_size, stack_size); // force stack size reset
|
|
compile_node(comp, pns_test_if_else->nodes[1]); // failure value
|
|
EMIT(label_assign, l_end);
|
|
}
|
|
|
|
void compile_lambdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
// TODO default params etc for lambda; possibly just use funcdef code
|
|
//mp_parse_node_t pn_params = pns->nodes[0];
|
|
//mp_parse_node_t pn_body = pns->nodes[1];
|
|
|
|
if (comp->pass == PASS_1) {
|
|
// create a new scope for this lambda
|
|
scope_t *s = scope_new_and_link(comp, SCOPE_LAMBDA, (mp_parse_node_t)pns, comp->scope_cur->emit_options);
|
|
// store the lambda scope so the compiling function (this one) can use it at each pass
|
|
pns->nodes[2] = (mp_parse_node_t)s;
|
|
}
|
|
|
|
// get the scope for this lambda
|
|
scope_t *this_scope = (scope_t*)pns->nodes[2];
|
|
|
|
// make the lambda
|
|
close_over_variables_etc(comp, this_scope, 0, 0);
|
|
}
|
|
|
|
void compile_or_test(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
int l_end = comp_next_label(comp);
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
for (int i = 0; i < n; i += 1) {
|
|
compile_node(comp, pns->nodes[i]);
|
|
if (i + 1 < n) {
|
|
EMIT(jump_if_true_or_pop, l_end);
|
|
}
|
|
}
|
|
EMIT(label_assign, l_end);
|
|
}
|
|
|
|
void compile_and_test(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
int l_end = comp_next_label(comp);
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
for (int i = 0; i < n; i += 1) {
|
|
compile_node(comp, pns->nodes[i]);
|
|
if (i + 1 < n) {
|
|
EMIT(jump_if_false_or_pop, l_end);
|
|
}
|
|
}
|
|
EMIT(label_assign, l_end);
|
|
}
|
|
|
|
void compile_not_test_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(unary_op, RT_UNARY_OP_NOT);
|
|
}
|
|
|
|
void compile_comparison(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
int stack_size = EMIT(get_stack_size);
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
compile_node(comp, pns->nodes[0]);
|
|
bool multi = (num_nodes > 3);
|
|
int l_fail = 0;
|
|
if (multi) {
|
|
l_fail = comp_next_label(comp);
|
|
}
|
|
for (int i = 1; i + 1 < num_nodes; i += 2) {
|
|
compile_node(comp, pns->nodes[i + 1]);
|
|
if (i + 2 < num_nodes) {
|
|
EMIT(dup_top);
|
|
EMIT(rot_three);
|
|
}
|
|
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_LESS)) {
|
|
EMIT(compare_op, RT_COMPARE_OP_LESS);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_MORE)) {
|
|
EMIT(compare_op, RT_COMPARE_OP_MORE);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_DBL_EQUAL)) {
|
|
EMIT(compare_op, RT_COMPARE_OP_EQUAL);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_LESS_EQUAL)) {
|
|
EMIT(compare_op, RT_COMPARE_OP_LESS_EQUAL);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_MORE_EQUAL)) {
|
|
EMIT(compare_op, RT_COMPARE_OP_MORE_EQUAL);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_NOT_EQUAL)) {
|
|
EMIT(compare_op, RT_COMPARE_OP_NOT_EQUAL);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_KW_IN)) {
|
|
EMIT(compare_op, RT_COMPARE_OP_IN);
|
|
} else if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[i])) {
|
|
mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[i];
|
|
int kind = MP_PARSE_NODE_STRUCT_KIND(pns2);
|
|
if (kind == PN_comp_op_not_in) {
|
|
EMIT(compare_op, RT_COMPARE_OP_NOT_IN);
|
|
} else if (kind == PN_comp_op_is) {
|
|
if (MP_PARSE_NODE_IS_NULL(pns2->nodes[0])) {
|
|
EMIT(compare_op, RT_COMPARE_OP_IS);
|
|
} else {
|
|
EMIT(compare_op, RT_COMPARE_OP_IS_NOT);
|
|
}
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
if (i + 2 < num_nodes) {
|
|
EMIT(jump_if_false_or_pop, l_fail);
|
|
}
|
|
}
|
|
if (multi) {
|
|
int l_end = comp_next_label(comp);
|
|
EMIT(jump, l_end);
|
|
EMIT(label_assign, l_fail);
|
|
EMIT(rot_two);
|
|
EMIT(pop_top);
|
|
EMIT(label_assign, l_end);
|
|
EMIT(set_stack_size, stack_size + 1); // force stack size
|
|
}
|
|
}
|
|
|
|
void compile_star_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
// TODO
|
|
assert(0);
|
|
compile_node(comp, pns->nodes[0]);
|
|
//EMIT(unary_op, "UNARY_STAR");
|
|
}
|
|
|
|
void compile_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
c_binary_op(comp, pns, RT_BINARY_OP_OR);
|
|
}
|
|
|
|
void compile_xor_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
c_binary_op(comp, pns, RT_BINARY_OP_XOR);
|
|
}
|
|
|
|
void compile_and_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
c_binary_op(comp, pns, RT_BINARY_OP_AND);
|
|
}
|
|
|
|
void compile_shift_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
compile_node(comp, pns->nodes[0]);
|
|
for (int i = 1; i + 1 < num_nodes; i += 2) {
|
|
compile_node(comp, pns->nodes[i + 1]);
|
|
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_DBL_LESS)) {
|
|
EMIT(binary_op, RT_BINARY_OP_LSHIFT);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_DBL_MORE)) {
|
|
EMIT(binary_op, RT_BINARY_OP_RSHIFT);
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void compile_arith_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
compile_node(comp, pns->nodes[0]);
|
|
for (int i = 1; i + 1 < num_nodes; i += 2) {
|
|
compile_node(comp, pns->nodes[i + 1]);
|
|
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_PLUS)) {
|
|
EMIT(binary_op, RT_BINARY_OP_ADD);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_MINUS)) {
|
|
EMIT(binary_op, RT_BINARY_OP_SUBTRACT);
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void compile_term(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
compile_node(comp, pns->nodes[0]);
|
|
for (int i = 1; i + 1 < num_nodes; i += 2) {
|
|
compile_node(comp, pns->nodes[i + 1]);
|
|
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_STAR)) {
|
|
EMIT(binary_op, RT_BINARY_OP_MULTIPLY);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_DBL_SLASH)) {
|
|
EMIT(binary_op, RT_BINARY_OP_FLOOR_DIVIDE);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_SLASH)) {
|
|
EMIT(binary_op, RT_BINARY_OP_TRUE_DIVIDE);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[i], MP_TOKEN_OP_PERCENT)) {
|
|
EMIT(binary_op, RT_BINARY_OP_MODULO);
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void compile_factor_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
compile_node(comp, pns->nodes[1]);
|
|
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_PLUS)) {
|
|
EMIT(unary_op, RT_UNARY_OP_POSITIVE);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_MINUS)) {
|
|
EMIT(unary_op, RT_UNARY_OP_NEGATIVE);
|
|
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_TILDE)) {
|
|
EMIT(unary_op, RT_UNARY_OP_INVERT);
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_struct_t *pns, bool is_method_call) {
|
|
// function to call is on top of stack
|
|
|
|
int old_n_arg_keyword = comp->n_arg_keyword;
|
|
bool old_have_star_arg = comp->have_star_arg;
|
|
bool old_have_dbl_star_arg = comp->have_dbl_star_arg;
|
|
comp->n_arg_keyword = 0;
|
|
comp->have_star_arg = false;
|
|
comp->have_dbl_star_arg = false;
|
|
|
|
compile_node(comp, pns->nodes[0]); // arguments to function call; can be null
|
|
|
|
// compute number of positional arguments
|
|
int n_positional = list_len(pns->nodes[0], PN_arglist) - comp->n_arg_keyword;
|
|
if (comp->have_star_arg) {
|
|
n_positional -= 1;
|
|
}
|
|
if (comp->have_dbl_star_arg) {
|
|
n_positional -= 1;
|
|
}
|
|
|
|
if (is_method_call) {
|
|
EMIT(call_method, n_positional, comp->n_arg_keyword, comp->have_star_arg, comp->have_dbl_star_arg);
|
|
} else {
|
|
EMIT(call_function, n_positional, comp->n_arg_keyword, comp->have_star_arg, comp->have_dbl_star_arg);
|
|
}
|
|
|
|
comp->n_arg_keyword = old_n_arg_keyword;
|
|
comp->have_star_arg = old_have_star_arg;
|
|
comp->have_dbl_star_arg = old_have_dbl_star_arg;
|
|
}
|
|
|
|
void compile_power_trailers(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
for (int i = 0; i < num_nodes; i++) {
|
|
if (i + 1 < num_nodes && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[i], PN_trailer_period) && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[i + 1], PN_trailer_paren)) {
|
|
// optimisation for method calls a.f(...), following PyPy
|
|
mp_parse_node_struct_t *pns_period = (mp_parse_node_struct_t*)pns->nodes[i];
|
|
mp_parse_node_struct_t *pns_paren = (mp_parse_node_struct_t*)pns->nodes[i + 1];
|
|
EMIT(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0])); // get the method
|
|
compile_trailer_paren_helper(comp, pns_paren, true);
|
|
i += 1;
|
|
} else {
|
|
compile_node(comp, pns->nodes[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void compile_power_dbl_star(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(binary_op, RT_BINARY_OP_POWER);
|
|
}
|
|
|
|
void compile_atom_string(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
// a list of strings
|
|
|
|
// check type of list (string or bytes) and count total number of bytes
|
|
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
int n_bytes = 0;
|
|
int string_kind = MP_PARSE_NODE_NULL;
|
|
for (int i = 0; i < n; i++) {
|
|
assert(MP_PARSE_NODE_IS_LEAF(pns->nodes[i]));
|
|
int pn_kind = MP_PARSE_NODE_LEAF_KIND(pns->nodes[i]);
|
|
assert(pn_kind == MP_PARSE_NODE_STRING || pn_kind == MP_PARSE_NODE_BYTES);
|
|
if (i == 0) {
|
|
string_kind = pn_kind;
|
|
} else if (pn_kind != string_kind) {
|
|
printf("SyntaxError: cannot mix bytes and nonbytes literals\n");
|
|
return;
|
|
}
|
|
const char *str = qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
|
|
n_bytes += strlen(str);
|
|
}
|
|
|
|
// allocate memory for concatenated string/bytes
|
|
char *cat_str = m_new(char, n_bytes + 1);
|
|
|
|
// concatenate string/bytes
|
|
char *s_dest = cat_str;
|
|
for (int i = 0; i < n; i++) {
|
|
const char *s = qstr_str(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i]));
|
|
size_t s_len = strlen(s);
|
|
memcpy(s_dest, s, s_len);
|
|
s_dest += s_len;
|
|
}
|
|
*s_dest = '\0';
|
|
|
|
EMIT(load_const_str, qstr_from_str_take(cat_str, n_bytes + 1), string_kind == MP_PARSE_NODE_BYTES);
|
|
}
|
|
|
|
// pns needs to have 2 nodes, first is lhs of comprehension, second is PN_comp_for node
|
|
void compile_comprehension(compiler_t *comp, mp_parse_node_struct_t *pns, scope_kind_t kind) {
|
|
assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2);
|
|
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for));
|
|
mp_parse_node_struct_t *pns_comp_for = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
|
|
if (comp->pass == PASS_1) {
|
|
// create a new scope for this comprehension
|
|
scope_t *s = scope_new_and_link(comp, kind, (mp_parse_node_t)pns, comp->scope_cur->emit_options);
|
|
// store the comprehension scope so the compiling function (this one) can use it at each pass
|
|
pns_comp_for->nodes[3] = (mp_parse_node_t)s;
|
|
}
|
|
|
|
// get the scope for this comprehension
|
|
scope_t *this_scope = (scope_t*)pns_comp_for->nodes[3];
|
|
|
|
// compile the comprehension
|
|
close_over_variables_etc(comp, this_scope, 0, 0);
|
|
|
|
compile_node(comp, pns_comp_for->nodes[1]); // source of the iterator
|
|
EMIT(get_iter);
|
|
EMIT(call_function, 1, 0, false, false);
|
|
}
|
|
|
|
void compile_atom_paren(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
|
|
// an empty tuple
|
|
c_tuple(comp, MP_PARSE_NODE_NULL, NULL);
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
|
|
pns = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
assert(!MP_PARSE_NODE_IS_NULL(pns->nodes[1]));
|
|
if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])) {
|
|
mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3b) {
|
|
// tuple of one item, with trailing comma
|
|
assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[0]));
|
|
c_tuple(comp, pns->nodes[0], NULL);
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_testlist_comp_3c) {
|
|
// tuple of many items
|
|
c_tuple(comp, pns->nodes[0], pns2);
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_comp_for) {
|
|
// generator expression
|
|
compile_comprehension(comp, pns, SCOPE_GEN_EXPR);
|
|
} else {
|
|
// tuple with 2 items
|
|
goto tuple_with_2_items;
|
|
}
|
|
} else {
|
|
// tuple with 2 items
|
|
tuple_with_2_items:
|
|
c_tuple(comp, MP_PARSE_NODE_NULL, pns);
|
|
}
|
|
} else {
|
|
// parenthesis around a single item, is just that item
|
|
compile_node(comp, pns->nodes[0]);
|
|
}
|
|
}
|
|
|
|
void compile_atom_bracket(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
|
|
// empty list
|
|
EMIT(build_list, 0);
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
|
|
mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
if (MP_PARSE_NODE_IS_STRUCT(pns2->nodes[1])) {
|
|
mp_parse_node_struct_t *pns3 = (mp_parse_node_struct_t*)pns2->nodes[1];
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_testlist_comp_3b) {
|
|
// list of one item, with trailing comma
|
|
assert(MP_PARSE_NODE_IS_NULL(pns3->nodes[0]));
|
|
compile_node(comp, pns2->nodes[0]);
|
|
EMIT(build_list, 1);
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_testlist_comp_3c) {
|
|
// list of many items
|
|
compile_node(comp, pns2->nodes[0]);
|
|
compile_generic_all_nodes(comp, pns3);
|
|
EMIT(build_list, 1 + MP_PARSE_NODE_STRUCT_NUM_NODES(pns3));
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns3) == PN_comp_for) {
|
|
// list comprehension
|
|
compile_comprehension(comp, pns2, SCOPE_LIST_COMP);
|
|
} else {
|
|
// list with 2 items
|
|
goto list_with_2_items;
|
|
}
|
|
} else {
|
|
// list with 2 items
|
|
list_with_2_items:
|
|
compile_node(comp, pns2->nodes[0]);
|
|
compile_node(comp, pns2->nodes[1]);
|
|
EMIT(build_list, 2);
|
|
}
|
|
} else {
|
|
// list with 1 item
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(build_list, 1);
|
|
}
|
|
}
|
|
|
|
void compile_atom_brace(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
mp_parse_node_t pn = pns->nodes[0];
|
|
if (MP_PARSE_NODE_IS_NULL(pn)) {
|
|
// empty dict
|
|
EMIT(build_map, 0);
|
|
} else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
|
|
pns = (mp_parse_node_struct_t*)pn;
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker_item) {
|
|
// dict with one element
|
|
EMIT(build_map, 1);
|
|
compile_node(comp, pn);
|
|
EMIT(store_map);
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed
|
|
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) {
|
|
// dict/set with multiple elements
|
|
|
|
// get tail elements (2nd, 3rd, ...)
|
|
mp_parse_node_t *nodes;
|
|
int n = list_get(&pns1->nodes[0], PN_dictorsetmaker_list2, &nodes);
|
|
|
|
// first element sets whether it's a dict or set
|
|
bool is_dict;
|
|
if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_dictorsetmaker_item)) {
|
|
// a dictionary
|
|
EMIT(build_map, 1 + n);
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(store_map);
|
|
is_dict = true;
|
|
} else {
|
|
// a set
|
|
compile_node(comp, pns->nodes[0]); // 1st value of set
|
|
is_dict = false;
|
|
}
|
|
|
|
// process rest of elements
|
|
for (int i = 0; i < n; i++) {
|
|
mp_parse_node_t pn = nodes[i];
|
|
bool is_key_value = MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_dictorsetmaker_item);
|
|
compile_node(comp, pn);
|
|
if (is_dict) {
|
|
if (!is_key_value) {
|
|
printf("SyntaxError?: expecting key:value for dictionary");
|
|
return;
|
|
}
|
|
EMIT(store_map);
|
|
} else {
|
|
if (is_key_value) {
|
|
printf("SyntaxError?: expecting just a value for set");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if it's a set, build it
|
|
if (!is_dict) {
|
|
EMIT(build_set, 1 + n);
|
|
}
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_comp_for) {
|
|
// dict/set comprehension
|
|
if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_dictorsetmaker_item)) {
|
|
// a dictionary comprehension
|
|
compile_comprehension(comp, pns, SCOPE_DICT_COMP);
|
|
} else {
|
|
// a set comprehension
|
|
compile_comprehension(comp, pns, SCOPE_SET_COMP);
|
|
}
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
} else {
|
|
// set with one element
|
|
goto set_with_one_element;
|
|
}
|
|
} else {
|
|
// set with one element
|
|
set_with_one_element:
|
|
compile_node(comp, pn);
|
|
EMIT(build_set, 1);
|
|
}
|
|
}
|
|
|
|
void compile_trailer_paren(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
compile_trailer_paren_helper(comp, pns, false);
|
|
}
|
|
|
|
void compile_trailer_bracket(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
// object who's index we want is on top of stack
|
|
compile_node(comp, pns->nodes[0]); // the index
|
|
EMIT(binary_op, RT_BINARY_OP_SUBSCR);
|
|
}
|
|
|
|
void compile_trailer_period(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
// object who's attribute we want is on top of stack
|
|
EMIT(load_attr, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // attribute to get
|
|
}
|
|
|
|
void compile_subscript_3_helper(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3); // should always be
|
|
mp_parse_node_t pn = pns->nodes[0];
|
|
if (MP_PARSE_NODE_IS_NULL(pn)) {
|
|
// [?:]
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
EMIT(build_slice, 2);
|
|
} else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
|
|
pns = (mp_parse_node_struct_t*)pn;
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3c) {
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
pn = pns->nodes[0];
|
|
if (MP_PARSE_NODE_IS_NULL(pn)) {
|
|
// [?::]
|
|
EMIT(build_slice, 2);
|
|
} else {
|
|
// [?::x]
|
|
compile_node(comp, pn);
|
|
EMIT(build_slice, 3);
|
|
}
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_subscript_3d) {
|
|
compile_node(comp, pns->nodes[0]);
|
|
assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
|
|
pns = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_sliceop); // should always be
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
|
|
// [?:x:]
|
|
EMIT(build_slice, 2);
|
|
} else {
|
|
// [?:x:x]
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(build_slice, 3);
|
|
}
|
|
} else {
|
|
// [?:x]
|
|
compile_node(comp, pn);
|
|
EMIT(build_slice, 2);
|
|
}
|
|
} else {
|
|
// [?:x]
|
|
compile_node(comp, pn);
|
|
EMIT(build_slice, 2);
|
|
}
|
|
}
|
|
|
|
void compile_subscript_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
compile_node(comp, pns->nodes[0]); // start of slice
|
|
assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
|
|
compile_subscript_3_helper(comp, (mp_parse_node_struct_t*)pns->nodes[1]);
|
|
}
|
|
|
|
void compile_subscript_3(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
compile_subscript_3_helper(comp, pns);
|
|
}
|
|
|
|
void compile_dictorsetmaker_item(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
// if this is called then we are compiling a dict key:value pair
|
|
compile_node(comp, pns->nodes[1]); // value
|
|
compile_node(comp, pns->nodes[0]); // key
|
|
}
|
|
|
|
void compile_classdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
qstr cname = compile_classdef_helper(comp, pns, comp->scope_cur->emit_options);
|
|
// store class object into class name
|
|
EMIT(store_id, cname);
|
|
}
|
|
|
|
void compile_arglist_star(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (comp->have_star_arg) {
|
|
printf("SyntaxError?: can't have multiple *x\n");
|
|
return;
|
|
}
|
|
comp->have_star_arg = true;
|
|
compile_node(comp, pns->nodes[0]);
|
|
}
|
|
|
|
void compile_arglist_dbl_star(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (comp->have_dbl_star_arg) {
|
|
printf("SyntaxError?: can't have multiple **x\n");
|
|
return;
|
|
}
|
|
comp->have_dbl_star_arg = true;
|
|
compile_node(comp, pns->nodes[0]);
|
|
}
|
|
|
|
void compile_argument(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should always be
|
|
mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_argument_3) {
|
|
if (!MP_PARSE_NODE_IS_ID(pns->nodes[0])) {
|
|
printf("SyntaxError?: lhs of keyword argument must be an id\n");
|
|
return;
|
|
}
|
|
EMIT(load_const_id, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]));
|
|
compile_node(comp, pns2->nodes[0]);
|
|
comp->n_arg_keyword += 1;
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_comp_for) {
|
|
compile_comprehension(comp, pns, SCOPE_GEN_EXPR);
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void compile_yield_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
|
|
if (comp->scope_cur->kind != SCOPE_FUNCTION) {
|
|
printf("SyntaxError: 'yield' outside function\n");
|
|
return;
|
|
}
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
EMIT(yield_value);
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_yield_arg_from)) {
|
|
pns = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(get_iter);
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
EMIT(yield_from);
|
|
} else {
|
|
compile_node(comp, pns->nodes[0]);
|
|
EMIT(yield_value);
|
|
}
|
|
}
|
|
|
|
typedef void (*compile_function_t)(compiler_t*, mp_parse_node_struct_t*);
|
|
static compile_function_t compile_function[] = {
|
|
NULL,
|
|
#define nc NULL
|
|
#define c(f) compile_##f
|
|
#define DEF_RULE(rule, comp, kind, arg...) comp,
|
|
#include "grammar.h"
|
|
#undef nc
|
|
#undef c
|
|
#undef DEF_RULE
|
|
};
|
|
|
|
void compile_node(compiler_t *comp, mp_parse_node_t pn) {
|
|
if (MP_PARSE_NODE_IS_NULL(pn)) {
|
|
// pass
|
|
} else if (MP_PARSE_NODE_IS_LEAF(pn)) {
|
|
int arg = MP_PARSE_NODE_LEAF_ARG(pn);
|
|
switch (MP_PARSE_NODE_LEAF_KIND(pn)) {
|
|
case MP_PARSE_NODE_ID: EMIT(load_id, arg); break;
|
|
case MP_PARSE_NODE_SMALL_INT: EMIT(load_const_small_int, arg); break;
|
|
case MP_PARSE_NODE_INTEGER: EMIT(load_const_int, arg); break;
|
|
case MP_PARSE_NODE_DECIMAL: EMIT(load_const_dec, arg); break;
|
|
case MP_PARSE_NODE_STRING: EMIT(load_const_str, arg, false); break;
|
|
case MP_PARSE_NODE_BYTES: EMIT(load_const_str, arg, true); break;
|
|
case MP_PARSE_NODE_TOKEN:
|
|
if (arg == MP_TOKEN_NEWLINE) {
|
|
// this can occur when file_input lets through a NEWLINE (eg if file starts with a newline)
|
|
// or when single_input lets through a NEWLINE (user enters a blank line)
|
|
// do nothing
|
|
} else {
|
|
EMIT(load_const_tok, arg);
|
|
}
|
|
break;
|
|
default: assert(0);
|
|
}
|
|
} else {
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
compile_function_t f = compile_function[MP_PARSE_NODE_STRUCT_KIND(pns)];
|
|
if (f == NULL) {
|
|
printf("node %u cannot be compiled\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns));
|
|
mp_parse_node_show(pn, 0);
|
|
assert(0);
|
|
} else {
|
|
f(comp, pns);
|
|
}
|
|
}
|
|
}
|
|
|
|
void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn, pn_kind_t pn_name, pn_kind_t pn_star, pn_kind_t pn_dbl_star, bool allow_annotations) {
|
|
// TODO verify that *k and **k are last etc
|
|
qstr param_name = 0;
|
|
mp_parse_node_t pn_annotation = MP_PARSE_NODE_NULL;
|
|
if (MP_PARSE_NODE_IS_ID(pn)) {
|
|
param_name = MP_PARSE_NODE_LEAF_ARG(pn);
|
|
if (comp->have_bare_star) {
|
|
// comes after a bare star, so doesn't count as a parameter
|
|
} else {
|
|
comp->scope_cur->num_params += 1;
|
|
}
|
|
} else {
|
|
assert(MP_PARSE_NODE_IS_STRUCT(pn));
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_name) {
|
|
param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
|
|
//int node_index = 1; unused
|
|
if (allow_annotations) {
|
|
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
|
|
// this parameter has an annotation
|
|
pn_annotation = pns->nodes[1];
|
|
}
|
|
//node_index = 2; unused
|
|
}
|
|
/* this is obsolete now that num dict/default params are calculated in compile_funcdef_param
|
|
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[node_index])) {
|
|
// this parameter has a default value
|
|
if (comp->have_bare_star) {
|
|
comp->scope_cur->num_dict_params += 1;
|
|
} else {
|
|
comp->scope_cur->num_default_params += 1;
|
|
}
|
|
}
|
|
*/
|
|
if (comp->have_bare_star) {
|
|
// comes after a bare star, so doesn't count as a parameter
|
|
} else {
|
|
comp->scope_cur->num_params += 1;
|
|
}
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_star) {
|
|
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
|
|
// bare star
|
|
// TODO see http://www.python.org/dev/peps/pep-3102/
|
|
comp->have_bare_star = true;
|
|
//assert(comp->scope_cur->num_dict_params == 0);
|
|
} else if (MP_PARSE_NODE_IS_ID(pns->nodes[0])) {
|
|
// named star
|
|
comp->scope_cur->flags |= SCOPE_FLAG_VARARGS;
|
|
param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
|
|
} else if (allow_annotations && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_tfpdef)) {
|
|
// named star with annotation
|
|
comp->scope_cur->flags |= SCOPE_FLAG_VARARGS;
|
|
pns = (mp_parse_node_struct_t*)pns->nodes[0];
|
|
param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
|
|
pn_annotation = pns->nodes[1];
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_dbl_star) {
|
|
param_name = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
|
|
if (allow_annotations && !MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
|
|
// this parameter has an annotation
|
|
pn_annotation = pns->nodes[1];
|
|
}
|
|
comp->scope_cur->flags |= SCOPE_FLAG_VARKEYWORDS;
|
|
} else {
|
|
// TODO anything to implement?
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
if (param_name != 0) {
|
|
if (!MP_PARSE_NODE_IS_NULL(pn_annotation)) {
|
|
// TODO this parameter has an annotation
|
|
}
|
|
bool added;
|
|
id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, param_name, &added);
|
|
if (!added) {
|
|
printf("SyntaxError?: same name used for parameter; %s\n", qstr_str(param_name));
|
|
return;
|
|
}
|
|
id_info->param = true;
|
|
id_info->kind = ID_INFO_KIND_LOCAL;
|
|
}
|
|
}
|
|
|
|
void compile_scope_func_param(compiler_t *comp, mp_parse_node_t pn) {
|
|
compile_scope_func_lambda_param(comp, pn, PN_typedargslist_name, PN_typedargslist_star, PN_typedargslist_dbl_star, true);
|
|
}
|
|
|
|
void compile_scope_lambda_param(compiler_t *comp, mp_parse_node_t pn) {
|
|
compile_scope_func_lambda_param(comp, pn, PN_varargslist_name, PN_varargslist_star, PN_varargslist_dbl_star, false);
|
|
}
|
|
|
|
void compile_scope_comp_iter(compiler_t *comp, mp_parse_node_t pn_iter, mp_parse_node_t pn_inner_expr, int l_top, int for_depth) {
|
|
tail_recursion:
|
|
if (MP_PARSE_NODE_IS_NULL(pn_iter)) {
|
|
// no more nested if/for; compile inner expression
|
|
compile_node(comp, pn_inner_expr);
|
|
if (comp->scope_cur->kind == SCOPE_LIST_COMP) {
|
|
EMIT(list_append, for_depth + 2);
|
|
} else if (comp->scope_cur->kind == SCOPE_DICT_COMP) {
|
|
EMIT(map_add, for_depth + 2);
|
|
} else if (comp->scope_cur->kind == SCOPE_SET_COMP) {
|
|
EMIT(set_add, for_depth + 2);
|
|
} else {
|
|
EMIT(yield_value);
|
|
EMIT(pop_top);
|
|
}
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_iter, PN_comp_if)) {
|
|
// if condition
|
|
mp_parse_node_struct_t *pns_comp_if = (mp_parse_node_struct_t*)pn_iter;
|
|
c_if_cond(comp, pns_comp_if->nodes[0], false, l_top);
|
|
pn_iter = pns_comp_if->nodes[1];
|
|
goto tail_recursion;
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_iter, PN_comp_for)) {
|
|
// for loop
|
|
mp_parse_node_struct_t *pns_comp_for2 = (mp_parse_node_struct_t*)pn_iter;
|
|
compile_node(comp, pns_comp_for2->nodes[1]);
|
|
int l_end2 = comp_next_label(comp);
|
|
int l_top2 = comp_next_label(comp);
|
|
EMIT(get_iter);
|
|
EMIT(label_assign, l_top2);
|
|
EMIT(for_iter, l_end2);
|
|
c_assign(comp, pns_comp_for2->nodes[0], ASSIGN_STORE);
|
|
compile_scope_comp_iter(comp, pns_comp_for2->nodes[2], pn_inner_expr, l_top2, for_depth + 1);
|
|
EMIT(jump, l_top2);
|
|
EMIT(label_assign, l_end2);
|
|
EMIT(for_iter_end);
|
|
} else {
|
|
// shouldn't happen
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void check_for_doc_string(compiler_t *comp, mp_parse_node_t pn) {
|
|
// see http://www.python.org/dev/peps/pep-0257/
|
|
|
|
// look for the first statement
|
|
if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_expr_stmt)) {
|
|
// a statement; fall through
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_file_input_2)) {
|
|
// file input; find the first non-newline node
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
|
|
int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
|
|
for (int i = 0; i < num_nodes; i++) {
|
|
pn = pns->nodes[i];
|
|
if (!(MP_PARSE_NODE_IS_LEAF(pn) && MP_PARSE_NODE_LEAF_KIND(pn) == MP_PARSE_NODE_TOKEN && MP_PARSE_NODE_LEAF_ARG(pn) == MP_TOKEN_NEWLINE)) {
|
|
// not a newline, so this is the first statement; finish search
|
|
break;
|
|
}
|
|
}
|
|
// if we didn't find a non-newline then it's okay to fall through; pn will be a newline and so doc-string test below will fail gracefully
|
|
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_suite_block_stmts)) {
|
|
// a list of statements; get the first one
|
|
pn = ((mp_parse_node_struct_t*)pn)->nodes[0];
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
// check the first statement for a doc string
|
|
if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_expr_stmt)) {
|
|
mp_parse_node_struct_t* pns = (mp_parse_node_struct_t*)pn;
|
|
if (MP_PARSE_NODE_IS_LEAF(pns->nodes[0])) {
|
|
int kind = MP_PARSE_NODE_LEAF_KIND(pns->nodes[0]);
|
|
if (kind == MP_PARSE_NODE_STRING) {
|
|
compile_node(comp, pns->nodes[0]); // a doc string
|
|
// store doc string
|
|
EMIT(store_id, MP_QSTR___doc__);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
|
|
comp->pass = pass;
|
|
comp->scope_cur = scope;
|
|
comp->next_label = 1;
|
|
EMIT(start_pass, pass, scope);
|
|
|
|
if (comp->pass == PASS_1) {
|
|
scope->stack_size = 0;
|
|
}
|
|
|
|
#if MICROPY_EMIT_CPYTHON
|
|
if (comp->pass == PASS_3) {
|
|
scope_print_info(scope);
|
|
}
|
|
#endif
|
|
|
|
// compile
|
|
if (scope->kind == SCOPE_MODULE) {
|
|
if (!comp->is_repl) {
|
|
check_for_doc_string(comp, scope->pn);
|
|
}
|
|
compile_node(comp, scope->pn);
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
EMIT(return_value);
|
|
} else if (scope->kind == SCOPE_FUNCTION) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
|
|
assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_funcdef);
|
|
|
|
// work out number of parameters, keywords and default parameters, and add them to the id_info array
|
|
// must be done before compiling the body so that arguments are numbered first (for LOAD_FAST etc)
|
|
if (comp->pass == PASS_1) {
|
|
comp->have_bare_star = false;
|
|
apply_to_single_or_list(comp, pns->nodes[1], PN_typedargslist, compile_scope_func_param);
|
|
}
|
|
|
|
assert(MP_PARSE_NODE_IS_NULL(pns->nodes[2])); // 2 is something...
|
|
|
|
compile_node(comp, pns->nodes[3]); // 3 is function body
|
|
// emit return if it wasn't the last opcode
|
|
if (!EMIT(last_emit_was_return_value)) {
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
EMIT(return_value);
|
|
}
|
|
} else if (scope->kind == SCOPE_LAMBDA) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
|
|
assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 3);
|
|
|
|
// work out number of parameters, keywords and default parameters, and add them to the id_info array
|
|
// must be done before compiling the body so that arguments are numbered first (for LOAD_FAST etc)
|
|
if (comp->pass == PASS_1) {
|
|
comp->have_bare_star = false;
|
|
apply_to_single_or_list(comp, pns->nodes[0], PN_varargslist, compile_scope_lambda_param);
|
|
}
|
|
|
|
compile_node(comp, pns->nodes[1]); // 1 is lambda body
|
|
EMIT(return_value);
|
|
} else if (scope->kind == SCOPE_LIST_COMP || scope->kind == SCOPE_DICT_COMP || scope->kind == SCOPE_SET_COMP || scope->kind == SCOPE_GEN_EXPR) {
|
|
// a bit of a hack at the moment
|
|
|
|
assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
|
|
assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 2);
|
|
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_comp_for));
|
|
mp_parse_node_struct_t *pns_comp_for = (mp_parse_node_struct_t*)pns->nodes[1];
|
|
|
|
qstr qstr_arg = qstr_from_str_static(".0");
|
|
if (comp->pass == PASS_1) {
|
|
bool added;
|
|
id_info_t *id_info = scope_find_or_add_id(comp->scope_cur, qstr_arg, &added);
|
|
assert(added);
|
|
id_info->kind = ID_INFO_KIND_LOCAL;
|
|
scope->num_params = 1;
|
|
}
|
|
|
|
if (scope->kind == SCOPE_LIST_COMP) {
|
|
EMIT(build_list, 0);
|
|
} else if (scope->kind == SCOPE_DICT_COMP) {
|
|
EMIT(build_map, 0);
|
|
} else if (scope->kind == SCOPE_SET_COMP) {
|
|
EMIT(build_set, 0);
|
|
}
|
|
|
|
int l_end = comp_next_label(comp);
|
|
int l_top = comp_next_label(comp);
|
|
EMIT(load_id, qstr_arg);
|
|
EMIT(label_assign, l_top);
|
|
EMIT(for_iter, l_end);
|
|
c_assign(comp, pns_comp_for->nodes[0], ASSIGN_STORE);
|
|
compile_scope_comp_iter(comp, pns_comp_for->nodes[2], pns->nodes[0], l_top, 0);
|
|
EMIT(jump, l_top);
|
|
EMIT(label_assign, l_end);
|
|
EMIT(for_iter_end);
|
|
|
|
if (scope->kind == SCOPE_GEN_EXPR) {
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
}
|
|
EMIT(return_value);
|
|
} else {
|
|
assert(scope->kind == SCOPE_CLASS);
|
|
assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
|
|
assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_classdef);
|
|
|
|
if (comp->pass == PASS_1) {
|
|
bool added;
|
|
id_info_t *id_info = scope_find_or_add_id(scope, MP_QSTR___class__, &added);
|
|
assert(added);
|
|
id_info->kind = ID_INFO_KIND_LOCAL;
|
|
id_info = scope_find_or_add_id(scope, MP_QSTR___locals__, &added);
|
|
assert(added);
|
|
id_info->kind = ID_INFO_KIND_LOCAL;
|
|
id_info->param = true;
|
|
scope->num_params = 1; // __locals__ is the parameter
|
|
}
|
|
|
|
EMIT(load_id, MP_QSTR___locals__);
|
|
EMIT(store_locals);
|
|
EMIT(load_id, MP_QSTR___name__);
|
|
EMIT(store_id, MP_QSTR___module__);
|
|
EMIT(load_const_id, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // 0 is class name
|
|
EMIT(store_id, MP_QSTR___qualname__);
|
|
|
|
check_for_doc_string(comp, pns->nodes[2]);
|
|
compile_node(comp, pns->nodes[2]); // 2 is class body
|
|
|
|
id_info_t *id = scope_find(scope, MP_QSTR___class__);
|
|
assert(id != NULL);
|
|
if (id->kind == ID_INFO_KIND_LOCAL) {
|
|
EMIT(load_const_tok, MP_TOKEN_KW_NONE);
|
|
} else {
|
|
#if MICROPY_EMIT_CPYTHON
|
|
EMIT(load_closure, MP_QSTR___class__, 0); // XXX check this is the correct local num
|
|
#else
|
|
EMIT(load_fast, MP_QSTR___class__, 0); // XXX check this is the correct local num
|
|
#endif
|
|
}
|
|
EMIT(return_value);
|
|
}
|
|
|
|
EMIT(end_pass);
|
|
|
|
}
|
|
|
|
void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
|
|
comp->pass = pass;
|
|
comp->scope_cur = scope;
|
|
comp->next_label = 1;
|
|
|
|
if (scope->kind != SCOPE_FUNCTION) {
|
|
printf("Error: inline assembler must be a function\n");
|
|
return;
|
|
}
|
|
|
|
if (comp->pass > PASS_1) {
|
|
EMIT_INLINE_ASM(start_pass, comp->pass, comp->scope_cur);
|
|
}
|
|
|
|
// get the function definition parse node
|
|
assert(MP_PARSE_NODE_IS_STRUCT(scope->pn));
|
|
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn;
|
|
assert(MP_PARSE_NODE_STRUCT_KIND(pns) == PN_funcdef);
|
|
|
|
//qstr f_id = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); // function name
|
|
|
|
// parameters are in pns->nodes[1]
|
|
if (comp->pass == PASS_2) {
|
|
mp_parse_node_t *pn_params;
|
|
int n_params = list_get(&pns->nodes[1], PN_typedargslist, &pn_params);
|
|
scope->num_params = EMIT_INLINE_ASM(count_params, n_params, pn_params);
|
|
}
|
|
|
|
assert(MP_PARSE_NODE_IS_NULL(pns->nodes[2])); // type
|
|
|
|
mp_parse_node_t pn_body = pns->nodes[3]; // body
|
|
mp_parse_node_t *nodes;
|
|
int num = list_get(&pn_body, PN_suite_block_stmts, &nodes);
|
|
|
|
if (comp->pass == PASS_3) {
|
|
//printf("----\n");
|
|
scope_print_info(scope);
|
|
}
|
|
|
|
for (int i = 0; i < num; i++) {
|
|
assert(MP_PARSE_NODE_IS_STRUCT(nodes[i]));
|
|
mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)nodes[i];
|
|
assert(MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_expr_stmt);
|
|
assert(MP_PARSE_NODE_IS_STRUCT(pns2->nodes[0]));
|
|
assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[1]));
|
|
pns2 = (mp_parse_node_struct_t*)pns2->nodes[0];
|
|
assert(MP_PARSE_NODE_STRUCT_KIND(pns2) == PN_power);
|
|
assert(MP_PARSE_NODE_IS_ID(pns2->nodes[0]));
|
|
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pns2->nodes[1], PN_trailer_paren));
|
|
assert(MP_PARSE_NODE_IS_NULL(pns2->nodes[2]));
|
|
qstr op = MP_PARSE_NODE_LEAF_ARG(pns2->nodes[0]);
|
|
pns2 = (mp_parse_node_struct_t*)pns2->nodes[1]; // PN_trailer_paren
|
|
mp_parse_node_t *pn_arg;
|
|
int n_args = list_get(&pns2->nodes[0], PN_arglist, &pn_arg);
|
|
|
|
// emit instructions
|
|
if (strcmp(qstr_str(op), "label") == 0) {
|
|
if (!(n_args == 1 && MP_PARSE_NODE_IS_ID(pn_arg[0]))) {
|
|
printf("SyntaxError: inline assembler 'label' requires 1 argument\n");
|
|
return;
|
|
}
|
|
int lab = comp_next_label(comp);
|
|
if (pass > PASS_1) {
|
|
EMIT_INLINE_ASM(label, lab, MP_PARSE_NODE_LEAF_ARG(pn_arg[0]));
|
|
}
|
|
} else {
|
|
if (pass > PASS_1) {
|
|
EMIT_INLINE_ASM(op, op, n_args, pn_arg);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (comp->pass > PASS_1) {
|
|
EMIT_INLINE_ASM(end_pass);
|
|
}
|
|
}
|
|
|
|
void compile_scope_compute_things(compiler_t *comp, scope_t *scope) {
|
|
// in functions, turn implicit globals into explicit globals
|
|
// compute the index of each local
|
|
scope->num_locals = 0;
|
|
for (int i = 0; i < scope->id_info_len; i++) {
|
|
id_info_t *id = &scope->id_info[i];
|
|
if (scope->kind == SCOPE_CLASS && id->qstr == MP_QSTR___class__) {
|
|
// __class__ is not counted as a local; if it's used then it becomes a ID_INFO_KIND_CELL
|
|
continue;
|
|
}
|
|
if (scope->kind >= SCOPE_FUNCTION && scope->kind <= SCOPE_GEN_EXPR && id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
|
|
id->kind = ID_INFO_KIND_GLOBAL_EXPLICIT;
|
|
}
|
|
// note: params always count for 1 local, even if they are a cell
|
|
if (id->param || id->kind == ID_INFO_KIND_LOCAL) {
|
|
id->local_num = scope->num_locals;
|
|
scope->num_locals += 1;
|
|
}
|
|
}
|
|
|
|
// compute the index of cell vars (freevars[idx] in CPython)
|
|
#if MICROPY_EMIT_CPYTHON
|
|
int num_cell = 0;
|
|
#endif
|
|
for (int i = 0; i < scope->id_info_len; i++) {
|
|
id_info_t *id = &scope->id_info[i];
|
|
#if MICROPY_EMIT_CPYTHON
|
|
// in CPython the cells are numbered starting from 0
|
|
if (id->kind == ID_INFO_KIND_CELL) {
|
|
id->local_num = num_cell;
|
|
num_cell += 1;
|
|
}
|
|
#else
|
|
// in Micro Python the cells come right after the fast locals
|
|
// parameters are not counted here, since they remain at the start
|
|
// of the locals, even if they are cell vars
|
|
if (!id->param && id->kind == ID_INFO_KIND_CELL) {
|
|
id->local_num = scope->num_locals;
|
|
scope->num_locals += 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// compute the index of free vars (freevars[idx] in CPython)
|
|
// make sure they are in the order of the parent scope
|
|
if (scope->parent != NULL) {
|
|
int num_free = 0;
|
|
for (int i = 0; i < scope->parent->id_info_len; i++) {
|
|
id_info_t *id = &scope->parent->id_info[i];
|
|
if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) {
|
|
for (int j = 0; j < scope->id_info_len; j++) {
|
|
id_info_t *id2 = &scope->id_info[j];
|
|
if (id2->kind == ID_INFO_KIND_FREE && id->qstr == id2->qstr) {
|
|
assert(!id2->param); // free vars should not be params
|
|
#if MICROPY_EMIT_CPYTHON
|
|
// in CPython the frees are numbered after the cells
|
|
id2->local_num = num_cell + num_free;
|
|
#else
|
|
// in Micro Python the frees come first, before the params
|
|
id2->local_num = num_free;
|
|
#endif
|
|
num_free += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if !MICROPY_EMIT_CPYTHON
|
|
// in Micro Python shift all other locals after the free locals
|
|
if (num_free > 0) {
|
|
for (int i = 0; i < scope->id_info_len; i++) {
|
|
id_info_t *id = &scope->id_info[i];
|
|
if (id->param || id->kind != ID_INFO_KIND_FREE) {
|
|
id->local_num += num_free;
|
|
}
|
|
}
|
|
scope->num_params += num_free; // free vars are counted as params for passing them into the function
|
|
scope->num_locals += num_free;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// compute flags
|
|
//scope->flags = 0; since we set some things in parameters
|
|
if (scope->kind != SCOPE_MODULE) {
|
|
scope->flags |= SCOPE_FLAG_NEWLOCALS;
|
|
}
|
|
if (scope->kind == SCOPE_FUNCTION || scope->kind == SCOPE_LAMBDA || scope->kind == SCOPE_LIST_COMP || scope->kind == SCOPE_DICT_COMP || scope->kind == SCOPE_SET_COMP || scope->kind == SCOPE_GEN_EXPR) {
|
|
assert(scope->parent != NULL);
|
|
scope->flags |= SCOPE_FLAG_OPTIMISED;
|
|
|
|
// TODO possibly other ways it can be nested
|
|
if (scope->parent->kind == SCOPE_FUNCTION || (scope->parent->kind == SCOPE_CLASS && scope->parent->parent->kind == SCOPE_FUNCTION)) {
|
|
scope->flags |= SCOPE_FLAG_NESTED;
|
|
}
|
|
}
|
|
int num_free = 0;
|
|
for (int i = 0; i < scope->id_info_len; i++) {
|
|
id_info_t *id = &scope->id_info[i];
|
|
if (id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE) {
|
|
num_free += 1;
|
|
}
|
|
}
|
|
if (num_free == 0) {
|
|
scope->flags |= SCOPE_FLAG_NOFREE;
|
|
}
|
|
}
|
|
|
|
mp_obj_t mp_compile(mp_parse_node_t pn, bool is_repl) {
|
|
compiler_t *comp = m_new(compiler_t, 1);
|
|
|
|
comp->is_repl = is_repl;
|
|
comp->had_error = false;
|
|
|
|
comp->break_label = 0;
|
|
comp->continue_label = 0;
|
|
comp->except_nest_level = 0;
|
|
comp->scope_head = NULL;
|
|
comp->scope_cur = NULL;
|
|
|
|
// optimise constants
|
|
pn = fold_constants(pn);
|
|
|
|
// set the outer scope
|
|
scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, pn, EMIT_OPT_NONE);
|
|
|
|
// compile pass 1
|
|
comp->emit = emit_pass1_new(MP_QSTR___class__);
|
|
comp->emit_method_table = &emit_pass1_method_table;
|
|
comp->emit_inline_asm = NULL;
|
|
comp->emit_inline_asm_method_table = NULL;
|
|
uint max_num_labels = 0;
|
|
for (scope_t *s = comp->scope_head; s != NULL && !comp->had_error; s = s->next) {
|
|
if (false) {
|
|
#if MICROPY_EMIT_INLINE_THUMB
|
|
} else if (s->emit_options == EMIT_OPT_ASM_THUMB) {
|
|
compile_scope_inline_asm(comp, s, PASS_1);
|
|
#endif
|
|
} else {
|
|
compile_scope(comp, s, PASS_1);
|
|
}
|
|
|
|
// update maximim number of labels needed
|
|
if (comp->next_label > max_num_labels) {
|
|
max_num_labels = comp->next_label;
|
|
}
|
|
}
|
|
|
|
// compute some things related to scope and identifiers
|
|
for (scope_t *s = comp->scope_head; s != NULL && !comp->had_error; s = s->next) {
|
|
compile_scope_compute_things(comp, s);
|
|
}
|
|
|
|
// finish with pass 1
|
|
emit_pass1_free(comp->emit);
|
|
|
|
// compile pass 2 and 3
|
|
#if !MICROPY_EMIT_CPYTHON
|
|
emit_t *emit_bc = NULL;
|
|
#if MICROPY_EMIT_NATIVE
|
|
emit_t *emit_native = NULL;
|
|
#endif
|
|
#if MICROPY_EMIT_INLINE_THUMB
|
|
emit_inline_asm_t *emit_inline_thumb = NULL;
|
|
#endif
|
|
#endif // !MICROPY_EMIT_CPYTHON
|
|
for (scope_t *s = comp->scope_head; s != NULL && !comp->had_error; s = s->next) {
|
|
if (false) {
|
|
// dummy
|
|
|
|
#if MICROPY_EMIT_INLINE_THUMB
|
|
} else if (s->emit_options == EMIT_OPT_ASM_THUMB) {
|
|
// inline assembly for thumb
|
|
if (emit_inline_thumb == NULL) {
|
|
emit_inline_thumb = emit_inline_thumb_new(max_num_labels);
|
|
}
|
|
comp->emit = NULL;
|
|
comp->emit_method_table = NULL;
|
|
comp->emit_inline_asm = emit_inline_thumb;
|
|
comp->emit_inline_asm_method_table = &emit_inline_thumb_method_table;
|
|
compile_scope_inline_asm(comp, s, PASS_2);
|
|
compile_scope_inline_asm(comp, s, PASS_3);
|
|
#endif
|
|
|
|
} else {
|
|
|
|
// choose the emit type
|
|
|
|
#if MICROPY_EMIT_CPYTHON
|
|
comp->emit = emit_cpython_new(max_num_labels);
|
|
comp->emit_method_table = &emit_cpython_method_table;
|
|
#else
|
|
switch (s->emit_options) {
|
|
|
|
#if MICROPY_EMIT_NATIVE
|
|
case EMIT_OPT_NATIVE_PYTHON:
|
|
case EMIT_OPT_VIPER:
|
|
#if MICROPY_EMIT_X64
|
|
if (emit_native == NULL) {
|
|
emit_native = emit_native_x64_new(max_num_labels);
|
|
}
|
|
comp->emit_method_table = &emit_native_x64_method_table;
|
|
#elif MICROPY_EMIT_THUMB
|
|
if (emit_native == NULL) {
|
|
emit_native = emit_native_thumb_new(max_num_labels);
|
|
}
|
|
comp->emit_method_table = &emit_native_thumb_method_table;
|
|
#endif
|
|
comp->emit = emit_native;
|
|
comp->emit_method_table->set_native_types(comp->emit, s->emit_options == EMIT_OPT_VIPER);
|
|
break;
|
|
#endif // MICROPY_EMIT_NATIVE
|
|
|
|
default:
|
|
if (emit_bc == NULL) {
|
|
emit_bc = emit_bc_new(max_num_labels);
|
|
}
|
|
comp->emit = emit_bc;
|
|
comp->emit_method_table = &emit_bc_method_table;
|
|
break;
|
|
}
|
|
#endif // !MICROPY_EMIT_CPYTHON
|
|
|
|
// compile pass 2 and pass 3
|
|
compile_scope(comp, s, PASS_2);
|
|
compile_scope(comp, s, PASS_3);
|
|
}
|
|
}
|
|
|
|
bool had_error = comp->had_error;
|
|
m_del_obj(compiler_t, comp);
|
|
|
|
if (had_error) {
|
|
// TODO return a proper error message
|
|
return mp_const_none;
|
|
} else {
|
|
#if MICROPY_EMIT_CPYTHON
|
|
// can't create code, so just return true
|
|
(void)module_scope; // to suppress warning that module_scope is unused
|
|
return mp_const_true;
|
|
#else
|
|
// return function that executes the outer module
|
|
return rt_make_function_from_id(module_scope->unique_code_id);
|
|
#endif
|
|
}
|
|
}
|