/* * This file includes functions to transform a concrete syntax tree (CST) to * an abstract syntax tree (AST). The main function is PyAST_FromNode(). * */ #include "Python.h" #include "Python-ast.h" #include "grammar.h" #include "node.h" #include "pyarena.h" #include "ast.h" #include "token.h" #include "parsetok.h" #include "graminit.h" #include /* Data structure used internally */ struct compiling { char *c_encoding; /* source encoding */ int c_future_unicode; /* __future__ unicode literals flag */ PyArena *c_arena; /* arena for allocating memeory */ const char *c_filename; /* filename */ }; static asdl_seq *seq_for_testlist(struct compiling *, const node *); static expr_ty ast_for_expr(struct compiling *, const node *); static stmt_ty ast_for_stmt(struct compiling *, const node *); static asdl_seq *ast_for_suite(struct compiling *, const node *); static asdl_seq *ast_for_exprlist(struct compiling *, const node *, expr_context_ty); static expr_ty ast_for_testlist(struct compiling *, const node *); static stmt_ty ast_for_classdef(struct compiling *, const node *, asdl_seq *); static expr_ty ast_for_testlist_gexp(struct compiling *, const node *); /* Note different signature for ast_for_call */ static expr_ty ast_for_call(struct compiling *, const node *, expr_ty); static PyObject *parsenumber(struct compiling *, const char *); static PyObject *parsestr(struct compiling *, const char *); static PyObject *parsestrplus(struct compiling *, const node *n); #ifndef LINENO #define LINENO(n) ((n)->n_lineno) #endif static identifier new_identifier(const char* n, PyArena *arena) { PyObject* id = PyString_InternFromString(n); if (id != NULL) PyArena_AddPyObject(arena, id); return id; } #define NEW_IDENTIFIER(n) new_identifier(STR(n), c->c_arena) /* This routine provides an invalid object for the syntax error. The outermost routine must unpack this error and create the proper object. We do this so that we don't have to pass the filename to everything function. XXX Maybe we should just pass the filename... */ static int ast_error(const node *n, const char *errstr) { PyObject *u = Py_BuildValue("zi", errstr, LINENO(n)); if (!u) return 0; PyErr_SetObject(PyExc_SyntaxError, u); Py_DECREF(u); return 0; } static void ast_error_finish(const char *filename) { PyObject *type, *value, *tback, *errstr, *loc, *tmp; long lineno; assert(PyErr_Occurred()); if (!PyErr_ExceptionMatches(PyExc_SyntaxError)) return; PyErr_Fetch(&type, &value, &tback); errstr = PyTuple_GetItem(value, 0); if (!errstr) return; Py_INCREF(errstr); lineno = PyInt_AsLong(PyTuple_GetItem(value, 1)); if (lineno == -1) { Py_DECREF(errstr); return; } Py_DECREF(value); loc = PyErr_ProgramText(filename, lineno); if (!loc) { Py_INCREF(Py_None); loc = Py_None; } tmp = Py_BuildValue("(zlOO)", filename, lineno, Py_None, loc); Py_DECREF(loc); if (!tmp) { Py_DECREF(errstr); return; } value = PyTuple_Pack(2, errstr, tmp); Py_DECREF(errstr); Py_DECREF(tmp); if (!value) return; PyErr_Restore(type, value, tback); } static int ast_warn(struct compiling *c, const node *n, char *msg) { if (PyErr_WarnExplicit(PyExc_SyntaxWarning, msg, c->c_filename, LINENO(n), NULL, NULL) < 0) { /* if -Werr, change it to a SyntaxError */ if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SyntaxWarning)) ast_error(n, msg); return 0; } return 1; } static int forbidden_check(struct compiling *c, const node *n, const char *x) { if (!strcmp(x, "None")) return ast_error(n, "cannot assign to None"); if (!strcmp(x, "__debug__")) return ast_error(n, "cannot assign to __debug__"); if (Py_Py3kWarningFlag) { if (!(strcmp(x, "True") && strcmp(x, "False")) && !ast_warn(c, n, "assignment to True or False is forbidden in 3.x")) return 0; if (!strcmp(x, "nonlocal") && !ast_warn(c, n, "nonlocal is a keyword in 3.x")) return 0; } return 1; } /* num_stmts() returns number of contained statements. Use this routine to determine how big a sequence is needed for the statements in a parse tree. Its raison d'etre is this bit of grammar: stmt: simple_stmt | compound_stmt simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE A simple_stmt can contain multiple small_stmt elements joined by semicolons. If the arg is a simple_stmt, the number of small_stmt elements is returned. */ static int num_stmts(const node *n) { int i, l; node *ch; switch (TYPE(n)) { case single_input: if (TYPE(CHILD(n, 0)) == NEWLINE) return 0; else return num_stmts(CHILD(n, 0)); case file_input: l = 0; for (i = 0; i < NCH(n); i++) { ch = CHILD(n, i); if (TYPE(ch) == stmt) l += num_stmts(ch); } return l; case stmt: return num_stmts(CHILD(n, 0)); case compound_stmt: return 1; case simple_stmt: return NCH(n) / 2; /* Divide by 2 to remove count of semi-colons */ case suite: if (NCH(n) == 1) return num_stmts(CHILD(n, 0)); else { l = 0; for (i = 2; i < (NCH(n) - 1); i++) l += num_stmts(CHILD(n, i)); return l; } default: { char buf[128]; sprintf(buf, "Non-statement found: %d %d", TYPE(n), NCH(n)); Py_FatalError(buf); } } assert(0); return 0; } /* Transform the CST rooted at node * to the appropriate AST */ mod_ty PyAST_FromNode(const node *n, PyCompilerFlags *flags, const char *filename, PyArena *arena) { int i, j, k, num; asdl_seq *stmts = NULL; stmt_ty s; node *ch; struct compiling c; if (flags && flags->cf_flags & PyCF_SOURCE_IS_UTF8) { c.c_encoding = "utf-8"; if (TYPE(n) == encoding_decl) { ast_error(n, "encoding declaration in Unicode string"); goto error; } } else if (TYPE(n) == encoding_decl) { c.c_encoding = STR(n); n = CHILD(n, 0); } else { c.c_encoding = NULL; } c.c_future_unicode = flags && flags->cf_flags & CO_FUTURE_UNICODE_LITERALS; c.c_arena = arena; c.c_filename = filename; k = 0; switch (TYPE(n)) { case file_input: stmts = asdl_seq_new(num_stmts(n), arena); if (!stmts) return NULL; for (i = 0; i < NCH(n) - 1; i++) { ch = CHILD(n, i); if (TYPE(ch) == NEWLINE) continue; REQ(ch, stmt); num = num_stmts(ch); if (num == 1) { s = ast_for_stmt(&c, ch); if (!s) goto error; asdl_seq_SET(stmts, k++, s); } else { ch = CHILD(ch, 0); REQ(ch, simple_stmt); for (j = 0; j < num; j++) { s = ast_for_stmt(&c, CHILD(ch, j * 2)); if (!s) goto error; asdl_seq_SET(stmts, k++, s); } } } return Module(stmts, arena); case eval_input: { expr_ty testlist_ast; /* XXX Why not gen_for here? */ testlist_ast = ast_for_testlist(&c, CHILD(n, 0)); if (!testlist_ast) goto error; return Expression(testlist_ast, arena); } case single_input: if (TYPE(CHILD(n, 0)) == NEWLINE) { stmts = asdl_seq_new(1, arena); if (!stmts) goto error; asdl_seq_SET(stmts, 0, Pass(n->n_lineno, n->n_col_offset, arena)); if (!asdl_seq_GET(stmts, 0)) goto error; return Interactive(stmts, arena); } else { n = CHILD(n, 0); num = num_stmts(n); stmts = asdl_seq_new(num, arena); if (!stmts) goto error; if (num == 1) { s = ast_for_stmt(&c, n); if (!s) goto error; asdl_seq_SET(stmts, 0, s); } else { /* Only a simple_stmt can contain multiple statements. */ REQ(n, simple_stmt); for (i = 0; i < NCH(n); i += 2) { if (TYPE(CHILD(n, i)) == NEWLINE) break; s = ast_for_stmt(&c, CHILD(n, i)); if (!s) goto error; asdl_seq_SET(stmts, i / 2, s); } } return Interactive(stmts, arena); } default: PyErr_Format(PyExc_SystemError, "invalid node %d for PyAST_FromNode", TYPE(n)); goto error; } error: ast_error_finish(filename); return NULL; } /* Return the AST repr. of the operator represented as syntax (|, ^, etc.) */ static operator_ty get_operator(const node *n) { switch (TYPE(n)) { case VBAR: return BitOr; case CIRCUMFLEX: return BitXor; case AMPER: return BitAnd; case LEFTSHIFT: return LShift; case RIGHTSHIFT: return RShift; case PLUS: return Add; case MINUS: return Sub; case STAR: return Mult; case SLASH: return Div; case DOUBLESLASH: return FloorDiv; case PERCENT: return Mod; default: return (operator_ty)0; } } /* Set the context ctx for expr_ty e, recursively traversing e. Only sets context for expr kinds that "can appear in assignment context" (according to ../Parser/Python.asdl). For other expr kinds, it sets an appropriate syntax error and returns false. */ static int set_context(struct compiling *c, expr_ty e, expr_context_ty ctx, const node *n) { asdl_seq *s = NULL; /* If a particular expression type can't be used for assign / delete, set expr_name to its name and an error message will be generated. */ const char* expr_name = NULL; /* The ast defines augmented store and load contexts, but the implementation here doesn't actually use them. The code may be a little more complex than necessary as a result. It also means that expressions in an augmented assignment have a Store context. Consider restructuring so that augmented assignment uses set_context(), too. */ assert(ctx != AugStore && ctx != AugLoad); switch (e->kind) { case Attribute_kind: if (ctx == Store && !forbidden_check(c, n, PyBytes_AS_STRING(e->v.Attribute.attr))) return 0; e->v.Attribute.ctx = ctx; break; case Subscript_kind: e->v.Subscript.ctx = ctx; break; case Name_kind: if (ctx == Store && !forbidden_check(c, n, PyBytes_AS_STRING(e->v.Name.id))) return 0; e->v.Name.ctx = ctx; break; case List_kind: e->v.List.ctx = ctx; s = e->v.List.elts; break; case Tuple_kind: if (asdl_seq_LEN(e->v.Tuple.elts)) { e->v.Tuple.ctx = ctx; s = e->v.Tuple.elts; } else { expr_name = "()"; } break; case Lambda_kind: expr_name = "lambda"; break; case Call_kind: expr_name = "function call"; break; case BoolOp_kind: case BinOp_kind: case UnaryOp_kind: expr_name = "operator"; break; case GeneratorExp_kind: expr_name = "generator expression"; break; case Yield_kind: expr_name = "yield expression"; break; case ListComp_kind: expr_name = "list comprehension"; break; case Dict_kind: case Num_kind: case Str_kind: expr_name = "literal"; break; case Compare_kind: expr_name = "comparison"; break; case Repr_kind: expr_name = "repr"; break; case IfExp_kind: expr_name = "conditional expression"; break; default: PyErr_Format(PyExc_SystemError, "unexpected expression in assignment %d (line %d)", e->kind, e->lineno); return 0; } /* Check for error string set by switch */ if (expr_name) { char buf[300]; PyOS_snprintf(buf, sizeof(buf), "can't %s %s", ctx == Store ? "assign to" : "delete", expr_name); return ast_error(n, buf); } /* If the LHS is a list or tuple, we need to set the assignment context for all the contained elements. */ if (s) { int i; for (i = 0; i < asdl_seq_LEN(s); i++) { if (!set_context(c, (expr_ty)asdl_seq_GET(s, i), ctx, n)) return 0; } } return 1; } static operator_ty ast_for_augassign(struct compiling *c, const node *n) { REQ(n, augassign); n = CHILD(n, 0); switch (STR(n)[0]) { case '+': return Add; case '-': return Sub; case '/': if (STR(n)[1] == '/') return FloorDiv; else return Div; case '%': return Mod; case '<': return LShift; case '>': return RShift; case '&': return BitAnd; case '^': return BitXor; case '|': return BitOr; case '*': if (STR(n)[1] == '*') return Pow; else return Mult; default: PyErr_Format(PyExc_SystemError, "invalid augassign: %s", STR(n)); return (operator_ty)0; } } static cmpop_ty ast_for_comp_op(struct compiling *c, const node *n) { /* comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is' |'is' 'not' */ REQ(n, comp_op); if (NCH(n) == 1) { n = CHILD(n, 0); switch (TYPE(n)) { case LESS: return Lt; case GREATER: return Gt; case EQEQUAL: /* == */ return Eq; case LESSEQUAL: return LtE; case GREATEREQUAL: return GtE; case NOTEQUAL: return NotEq; case NAME: if (strcmp(STR(n), "in") == 0) return In; if (strcmp(STR(n), "is") == 0) return Is; default: PyErr_Format(PyExc_SystemError, "invalid comp_op: %s", STR(n)); return (cmpop_ty)0; } } else if (NCH(n) == 2) { /* handle "not in" and "is not" */ switch (TYPE(CHILD(n, 0))) { case NAME: if (strcmp(STR(CHILD(n, 1)), "in") == 0) return NotIn; if (strcmp(STR(CHILD(n, 0)), "is") == 0) return IsNot; default: PyErr_Format(PyExc_SystemError, "invalid comp_op: %s %s", STR(CHILD(n, 0)), STR(CHILD(n, 1))); return (cmpop_ty)0; } } PyErr_Format(PyExc_SystemError, "invalid comp_op: has %d children", NCH(n)); return (cmpop_ty)0; } static asdl_seq * seq_for_testlist(struct compiling *c, const node *n) { /* testlist: test (',' test)* [','] */ asdl_seq *seq; expr_ty expression; int i; assert(TYPE(n) == testlist || TYPE(n) == listmaker || TYPE(n) == testlist_gexp || TYPE(n) == testlist_safe || TYPE(n) == testlist1); seq = asdl_seq_new((NCH(n) + 1) / 2, c->c_arena); if (!seq) return NULL; for (i = 0; i < NCH(n); i += 2) { assert(TYPE(CHILD(n, i)) == test || TYPE(CHILD(n, i)) == old_test); expression = ast_for_expr(c, CHILD(n, i)); if (!expression) return NULL; assert(i / 2 < seq->size); asdl_seq_SET(seq, i / 2, expression); } return seq; } static expr_ty compiler_complex_args(struct compiling *c, const node *n) { int i, len = (NCH(n) + 1) / 2; expr_ty result; asdl_seq *args = asdl_seq_new(len, c->c_arena); if (!args) return NULL; /* fpdef: NAME | '(' fplist ')' fplist: fpdef (',' fpdef)* [','] */ REQ(n, fplist); for (i = 0; i < len; i++) { PyObject *arg_id; const node *fpdef_node = CHILD(n, 2*i); const node *child; expr_ty arg; set_name: /* fpdef_node is either a NAME or an fplist */ child = CHILD(fpdef_node, 0); if (TYPE(child) == NAME) { if (!forbidden_check(c, n, STR(child))) return NULL; arg_id = NEW_IDENTIFIER(child); if (!arg_id) return NULL; arg = Name(arg_id, Store, LINENO(child), child->n_col_offset, c->c_arena); } else { assert(TYPE(fpdef_node) == fpdef); /* fpdef_node[0] is not a name, so it must be '(', get CHILD[1] */ child = CHILD(fpdef_node, 1); assert(TYPE(child) == fplist); /* NCH == 1 means we have (x), we need to elide the extra parens */ if (NCH(child) == 1) { fpdef_node = CHILD(child, 0); assert(TYPE(fpdef_node) == fpdef); goto set_name; } arg = compiler_complex_args(c, child); } asdl_seq_SET(args, i, arg); } result = Tuple(args, Store, LINENO(n), n->n_col_offset, c->c_arena); if (!set_context(c, result, Store, n)) return NULL; return result; } /* Create AST for argument list. */ static arguments_ty ast_for_arguments(struct compiling *c, const node *n) { /* parameters: '(' [varargslist] ')' varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) | fpdef ['=' test] (',' fpdef ['=' test])* [','] */ int i, j, k, n_args = 0, n_defaults = 0, found_default = 0; asdl_seq *args, *defaults; identifier vararg = NULL, kwarg = NULL; node *ch; if (TYPE(n) == parameters) { if (NCH(n) == 2) /* () as argument list */ return arguments(NULL, NULL, NULL, NULL, c->c_arena); n = CHILD(n, 1); } REQ(n, varargslist); /* first count the number of normal args & defaults */ for (i = 0; i < NCH(n); i++) { ch = CHILD(n, i); if (TYPE(ch) == fpdef) n_args++; if (TYPE(ch) == EQUAL) n_defaults++; } args = (n_args ? asdl_seq_new(n_args, c->c_arena) : NULL); if (!args && n_args) return NULL; /* Don't need to goto error; no objects allocated */ defaults = (n_defaults ? asdl_seq_new(n_defaults, c->c_arena) : NULL); if (!defaults && n_defaults) return NULL; /* Don't need to goto error; no objects allocated */ /* fpdef: NAME | '(' fplist ')' fplist: fpdef (',' fpdef)* [','] */ i = 0; j = 0; /* index for defaults */ k = 0; /* index for args */ while (i < NCH(n)) { ch = CHILD(n, i); switch (TYPE(ch)) { case fpdef: handle_fpdef: /* XXX Need to worry about checking if TYPE(CHILD(n, i+1)) is anything other than EQUAL or a comma? */ /* XXX Should NCH(n) check be made a separate check? */ if (i + 1 < NCH(n) && TYPE(CHILD(n, i + 1)) == EQUAL) { expr_ty expression = ast_for_expr(c, CHILD(n, i + 2)); if (!expression) goto error; assert(defaults != NULL); asdl_seq_SET(defaults, j++, expression); i += 2; found_default = 1; } else if (found_default) { ast_error(n, "non-default argument follows default argument"); goto error; } if (NCH(ch) == 3) { ch = CHILD(ch, 1); /* def foo((x)): is not complex, special case. */ if (NCH(ch) != 1) { /* We have complex arguments, setup for unpacking. */ if (Py_Py3kWarningFlag && !ast_warn(c, ch, "tuple parameter unpacking has been removed in 3.x")) goto error; asdl_seq_SET(args, k++, compiler_complex_args(c, ch)); if (!asdl_seq_GET(args, k-1)) goto error; } else { /* def foo((x)): setup for checking NAME below. */ /* Loop because there can be many parens and tuple unpacking mixed in. */ ch = CHILD(ch, 0); assert(TYPE(ch) == fpdef); goto handle_fpdef; } } if (TYPE(CHILD(ch, 0)) == NAME) { PyObject *id; expr_ty name; if (!forbidden_check(c, n, STR(CHILD(ch, 0)))) goto error; id = NEW_IDENTIFIER(CHILD(ch, 0)); if (!id) goto error; name = Name(id, Param, LINENO(ch), ch->n_col_offset, c->c_arena); if (!name) goto error; asdl_seq_SET(args, k++, name); } i += 2; /* the name and the comma */ break; case STAR: if (!forbidden_check(c, CHILD(n, i+1), STR(CHILD(n, i+1)))) goto error; vararg = NEW_IDENTIFIER(CHILD(n, i+1)); if (!vararg) goto error; i += 3; break; case DOUBLESTAR: if (!forbidden_check(c, CHILD(n, i+1), STR(CHILD(n, i+1)))) goto error; kwarg = NEW_IDENTIFIER(CHILD(n, i+1)); if (!kwarg) goto error; i += 3; break; default: PyErr_Format(PyExc_SystemError, "unexpected node in varargslist: %d @ %d", TYPE(ch), i); goto error; } } return arguments(args, vararg, kwarg, defaults, c->c_arena); error: Py_XDECREF(vararg); Py_XDECREF(kwarg); return NULL; } static expr_ty ast_for_dotted_name(struct compiling *c, const node *n) { expr_ty e; identifier id; int lineno, col_offset; int i; REQ(n, dotted_name); lineno = LINENO(n); col_offset = n->n_col_offset; id = NEW_IDENTIFIER(CHILD(n, 0)); if (!id) return NULL; e = Name(id, Load, lineno, col_offset, c->c_arena); if (!e) return NULL; for (i = 2; i < NCH(n); i+=2) { id = NEW_IDENTIFIER(CHILD(n, i)); if (!id) return NULL; e = Attribute(e, id, Load, lineno, col_offset, c->c_arena); if (!e) return NULL; } return e; } static expr_ty ast_for_decorator(struct compiling *c, const node *n) { /* decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE */ expr_ty d = NULL; expr_ty name_expr; REQ(n, decorator); REQ(CHILD(n, 0), AT); REQ(RCHILD(n, -1), NEWLINE); name_expr = ast_for_dotted_name(c, CHILD(n, 1)); if (!name_expr) return NULL; if (NCH(n) == 3) { /* No arguments */ d = name_expr; name_expr = NULL; } else if (NCH(n) == 5) { /* Call with no arguments */ d = Call(name_expr, NULL, NULL, NULL, NULL, LINENO(n), n->n_col_offset, c->c_arena); if (!d) return NULL; name_expr = NULL; } else { d = ast_for_call(c, CHILD(n, 3), name_expr); if (!d) return NULL; name_expr = NULL; } return d; } static asdl_seq* ast_for_decorators(struct compiling *c, const node *n) { asdl_seq* decorator_seq; expr_ty d; int i; REQ(n, decorators); decorator_seq = asdl_seq_new(NCH(n), c->c_arena); if (!decorator_seq) return NULL; for (i = 0; i < NCH(n); i++) { d = ast_for_decorator(c, CHILD(n, i)); if (!d) return NULL; asdl_seq_SET(decorator_seq, i, d); } return decorator_seq; } static stmt_ty ast_for_funcdef(struct compiling *c, const node *n, asdl_seq *decorator_seq) { /* funcdef: 'def' NAME parameters ':' suite */ identifier name; arguments_ty args; asdl_seq *body; int name_i = 1; REQ(n, funcdef); name = NEW_IDENTIFIER(CHILD(n, name_i)); if (!name) return NULL; else if (!forbidden_check(c, CHILD(n, name_i), STR(CHILD(n, name_i)))) return NULL; args = ast_for_arguments(c, CHILD(n, name_i + 1)); if (!args) return NULL; body = ast_for_suite(c, CHILD(n, name_i + 3)); if (!body) return NULL; return FunctionDef(name, args, body, decorator_seq, LINENO(n), n->n_col_offset, c->c_arena); } static stmt_ty ast_for_decorated(struct compiling *c, const node *n) { /* decorated: decorators (classdef | funcdef) */ stmt_ty thing = NULL; asdl_seq *decorator_seq = NULL; REQ(n, decorated); decorator_seq = ast_for_decorators(c, CHILD(n, 0)); if (!decorator_seq) return NULL; assert(TYPE(CHILD(n, 1)) == funcdef || TYPE(CHILD(n, 1)) == classdef); if (TYPE(CHILD(n, 1)) == funcdef) { thing = ast_for_funcdef(c, CHILD(n, 1), decorator_seq); } else if (TYPE(CHILD(n, 1)) == classdef) { thing = ast_for_classdef(c, CHILD(n, 1), decorator_seq); } /* we count the decorators in when talking about the class' or function's line number */ if (thing) { thing->lineno = LINENO(n); thing->col_offset = n->n_col_offset; } return thing; } static expr_ty ast_for_lambdef(struct compiling *c, const node *n) { /* lambdef: 'lambda' [varargslist] ':' test */ arguments_ty args; expr_ty expression; if (NCH(n) == 3) { args = arguments(NULL, NULL, NULL, NULL, c->c_arena); if (!args) return NULL; expression = ast_for_expr(c, CHILD(n, 2)); if (!expression) return NULL; } else { args = ast_for_arguments(c, CHILD(n, 1)); if (!args) return NULL; expression = ast_for_expr(c, CHILD(n, 3)); if (!expression) return NULL; } return Lambda(args, expression, LINENO(n), n->n_col_offset, c->c_arena); } static expr_ty ast_for_ifexpr(struct compiling *c, const node *n) { /* test: or_test 'if' or_test 'else' test */ expr_ty expression, body, orelse; assert(NCH(n) == 5); body = ast_for_expr(c, CHILD(n, 0)); if (!body) return NULL; expression = ast_for_expr(c, CHILD(n, 2)); if (!expression) return NULL; orelse = ast_for_expr(c, CHILD(n, 4)); if (!orelse) return NULL; return IfExp(expression, body, orelse, LINENO(n), n->n_col_offset, c->c_arena); } /* XXX(nnorwitz): the listcomp and genexpr code should be refactored so there is only a single version. Possibly for loops can also re-use the code. */ /* Count the number of 'for' loop in a list comprehension. Helper for ast_for_listcomp(). */ static int count_list_fors(struct compiling *c, const node *n) { int n_fors = 0; node *ch = CHILD(n, 1); count_list_for: n_fors++; REQ(ch, list_for); if (NCH(ch) == 5) ch = CHILD(ch, 4); else return n_fors; count_list_iter: REQ(ch, list_iter); ch = CHILD(ch, 0); if (TYPE(ch) == list_for) goto count_list_for; else if (TYPE(ch) == list_if) { if (NCH(ch) == 3) { ch = CHILD(ch, 2); goto count_list_iter; } else return n_fors; } /* Should never be reached */ PyErr_SetString(PyExc_SystemError, "logic error in count_list_fors"); return -1; } /* Count the number of 'if' statements in a list comprehension. Helper for ast_for_listcomp(). */ static int count_list_ifs(struct compiling *c, const node *n) { int n_ifs = 0; count_list_iter: REQ(n, list_iter); if (TYPE(CHILD(n, 0)) == list_for) return n_ifs; n = CHILD(n, 0); REQ(n, list_if); n_ifs++; if (NCH(n) == 2) return n_ifs; n = CHILD(n, 2); goto count_list_iter; } static expr_ty ast_for_listcomp(struct compiling *c, const node *n) { /* listmaker: test ( list_for | (',' test)* [','] ) list_for: 'for' exprlist 'in' testlist_safe [list_iter] list_iter: list_for | list_if list_if: 'if' test [list_iter] testlist_safe: test [(',' test)+ [',']] */ expr_ty elt; asdl_seq *listcomps; int i, n_fors; node *ch; REQ(n, listmaker); assert(NCH(n) > 1); elt = ast_for_expr(c, CHILD(n, 0)); if (!elt) return NULL; n_fors = count_list_fors(c, n); if (n_fors == -1) return NULL; listcomps = asdl_seq_new(n_fors, c->c_arena); if (!listcomps) return NULL; ch = CHILD(n, 1); for (i = 0; i < n_fors; i++) { comprehension_ty lc; asdl_seq *t; expr_ty expression; node *for_ch; REQ(ch, list_for); for_ch = CHILD(ch, 1); t = ast_for_exprlist(c, for_ch, Store); if (!t) return NULL; expression = ast_for_testlist(c, CHILD(ch, 3)); if (!expression) return NULL; /* Check the # of children rather than the length of t, since [x for x, in ... ] has 1 element in t, but still requires a Tuple. */ if (NCH(for_ch) == 1) lc = comprehension((expr_ty)asdl_seq_GET(t, 0), expression, NULL, c->c_arena); else lc = comprehension(Tuple(t, Store, LINENO(ch), ch->n_col_offset, c->c_arena), expression, NULL, c->c_arena); if (!lc) return NULL; if (NCH(ch) == 5) { int j, n_ifs; asdl_seq *ifs; expr_ty list_for_expr; ch = CHILD(ch, 4); n_ifs = count_list_ifs(c, ch); if (n_ifs == -1) return NULL; ifs = asdl_seq_new(n_ifs, c->c_arena); if (!ifs) return NULL; for (j = 0; j < n_ifs; j++) { REQ(ch, list_iter); ch = CHILD(ch, 0); REQ(ch, list_if); list_for_expr = ast_for_expr(c, CHILD(ch, 1)); if (!list_for_expr) return NULL; asdl_seq_SET(ifs, j, list_for_expr); if (NCH(ch) == 3) ch = CHILD(ch, 2); } /* on exit, must guarantee that ch is a list_for */ if (TYPE(ch) == list_iter) ch = CHILD(ch, 0); lc->ifs = ifs; } asdl_seq_SET(listcomps, i, lc); } return ListComp(elt, listcomps, LINENO(n), n->n_col_offset, c->c_arena); } /* Count the number of 'for' loops in a generator expression. Helper for ast_for_genexp(). */ static int count_gen_fors(struct compiling *c, const node *n) { int n_fors = 0; node *ch = CHILD(n, 1); count_gen_for: n_fors++; REQ(ch, gen_for); if (NCH(ch) == 5) ch = CHILD(ch, 4); else return n_fors; count_gen_iter: REQ(ch, gen_iter); ch = CHILD(ch, 0); if (TYPE(ch) == gen_for) goto count_gen_for; else if (TYPE(ch) == gen_if) { if (NCH(ch) == 3) { ch = CHILD(ch, 2); goto count_gen_iter; } else return n_fors; } /* Should never be reached */ PyErr_SetString(PyExc_SystemError, "logic error in count_gen_fors"); return -1; } /* Count the number of 'if' statements in a generator expression. Helper for ast_for_genexp(). */ static int count_gen_ifs(struct compiling *c, const node *n) { int n_ifs = 0; while (1) { REQ(n, gen_iter); if (TYPE(CHILD(n, 0)) == gen_for) return n_ifs; n = CHILD(n, 0); REQ(n, gen_if); n_ifs++; if (NCH(n) == 2) return n_ifs; n = CHILD(n, 2); } } /* TODO(jhylton): Combine with list comprehension code? */ static expr_ty ast_for_genexp(struct compiling *c, const node *n) { /* testlist_gexp: test ( gen_for | (',' test)* [','] ) argument: [test '='] test [gen_for] # Really [keyword '='] test */ expr_ty elt; asdl_seq *genexps; int i, n_fors; node *ch; assert(TYPE(n) == (testlist_gexp) || TYPE(n) == (argument)); assert(NCH(n) > 1); elt = ast_for_expr(c, CHILD(n, 0)); if (!elt) return NULL; n_fors = count_gen_fors(c, n); if (n_fors == -1) return NULL; genexps = asdl_seq_new(n_fors, c->c_arena); if (!genexps) return NULL; ch = CHILD(n, 1); for (i = 0; i < n_fors; i++) { comprehension_ty ge; asdl_seq *t; expr_ty expression; node *for_ch; REQ(ch, gen_for); for_ch = CHILD(ch, 1); t = ast_for_exprlist(c, for_ch, Store); if (!t) return NULL; expression = ast_for_expr(c, CHILD(ch, 3)); if (!expression) return NULL; /* Check the # of children rather than the length of t, since (x for x, in ...) has 1 element in t, but still requires a Tuple. */ if (NCH(for_ch) == 1) ge = comprehension((expr_ty)asdl_seq_GET(t, 0), expression, NULL, c->c_arena); else ge = comprehension(Tuple(t, Store, LINENO(ch), ch->n_col_offset, c->c_arena), expression, NULL, c->c_arena); if (!ge) return NULL; if (NCH(ch) == 5) { int j, n_ifs; asdl_seq *ifs; ch = CHILD(ch, 4); n_ifs = count_gen_ifs(c, ch); if (n_ifs == -1) return NULL; ifs = asdl_seq_new(n_ifs, c->c_arena); if (!ifs) return NULL; for (j = 0; j < n_ifs; j++) { REQ(ch, gen_iter); ch = CHILD(ch, 0); REQ(ch, gen_if); expression = ast_for_expr(c, CHILD(ch, 1)); if (!expression) return NULL; asdl_seq_SET(ifs, j, expression); if (NCH(ch) == 3) ch = CHILD(ch, 2); } /* on exit, must guarantee that ch is a gen_for */ if (TYPE(ch) == gen_iter) ch = CHILD(ch, 0); ge->ifs = ifs; } asdl_seq_SET(genexps, i, ge); } return GeneratorExp(elt, genexps, LINENO(n), n->n_col_offset, c->c_arena); } static expr_ty ast_for_atom(struct compiling *c, const node *n) { /* atom: '(' [yield_expr|testlist_gexp] ')' | '[' [listmaker] ']' | '{' [dictmaker] '}' | '`' testlist '`' | NAME | NUMBER | STRING+ */ node *ch = CHILD(n, 0); switch (TYPE(ch)) { case NAME: { /* All names start in Load context, but may later be changed. */ PyObject *name = NEW_IDENTIFIER(ch); if (!name) return NULL; return Name(name, Load, LINENO(n), n->n_col_offset, c->c_arena); } case STRING: { PyObject *str = parsestrplus(c, n); if (!str) { #ifdef Py_USING_UNICODE if (PyErr_ExceptionMatches(PyExc_UnicodeError)){ PyObject *type, *value, *tback, *errstr; PyErr_Fetch(&type, &value, &tback); errstr = PyObject_Str(value); if (errstr) { char *s = ""; char buf[128]; s = PyString_AsString(errstr); PyOS_snprintf(buf, sizeof(buf), "(unicode error) %s", s); ast_error(n, buf); Py_DECREF(errstr); } else { ast_error(n, "(unicode error) unknown error"); } Py_DECREF(type); Py_DECREF(value); Py_XDECREF(tback); } #endif return NULL; } PyArena_AddPyObject(c->c_arena, str); return Str(str, LINENO(n), n->n_col_offset, c->c_arena); } case NUMBER: { PyObject *pynum = parsenumber(c, STR(ch)); if (!pynum) return NULL; PyArena_AddPyObject(c->c_arena, pynum); return Num(pynum, LINENO(n), n->n_col_offset, c->c_arena); } case LPAR: /* some parenthesized expressions */ ch = CHILD(n, 1); if (TYPE(ch) == RPAR) return Tuple(NULL, Load, LINENO(n), n->n_col_offset, c->c_arena); if (TYPE(ch) == yield_expr) return ast_for_expr(c, ch); return ast_for_testlist_gexp(c, ch); case LSQB: /* list (or list comprehension) */ ch = CHILD(n, 1); if (TYPE(ch) == RSQB) return List(NULL, Load, LINENO(n), n->n_col_offset, c->c_arena); REQ(ch, listmaker); if (NCH(ch) == 1 || TYPE(CHILD(ch, 1)) == COMMA) { asdl_seq *elts = seq_for_testlist(c, ch); if (!elts) return NULL; return List(elts, Load, LINENO(n), n->n_col_offset, c->c_arena); } else return ast_for_listcomp(c, ch); case LBRACE: { /* dictmaker: test ':' test (',' test ':' test)* [','] */ int i, size; asdl_seq *keys, *values; ch = CHILD(n, 1); size = (NCH(ch) + 1) / 4; /* +1 in case no trailing comma */ keys = asdl_seq_new(size, c->c_arena); if (!keys) return NULL; values = asdl_seq_new(size, c->c_arena); if (!values) return NULL; for (i = 0; i < NCH(ch); i += 4) { expr_ty expression; expression = ast_for_expr(c, CHILD(ch, i)); if (!expression) return NULL; asdl_seq_SET(keys, i / 4, expression); expression = ast_for_expr(c, CHILD(ch, i + 2)); if (!expression) return NULL; asdl_seq_SET(values, i / 4, expression); } return Dict(keys, values, LINENO(n), n->n_col_offset, c->c_arena); } case BACKQUOTE: { /* repr */ expr_ty expression; if (Py_Py3kWarningFlag && !ast_warn(c, n, "backquote not supported in 3.x; use repr()")) return NULL; expression = ast_for_testlist(c, CHILD(n, 1)); if (!expression) return NULL; return Repr(expression, LINENO(n), n->n_col_offset, c->c_arena); } default: PyErr_Format(PyExc_SystemError, "unhandled atom %d", TYPE(ch)); return NULL; } } static slice_ty ast_for_slice(struct compiling *c, const node *n) { node *ch; expr_ty lower = NULL, upper = NULL, step = NULL; REQ(n, subscript); /* subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop] sliceop: ':' [test] */ ch = CHILD(n, 0); if (TYPE(ch) == DOT) return Ellipsis(c->c_arena); if (NCH(n) == 1 && TYPE(ch) == test) { /* 'step' variable hold no significance in terms of being used over other vars */ step = ast_for_expr(c, ch); if (!step) return NULL; return Index(step, c->c_arena); } if (TYPE(ch) == test) { lower = ast_for_expr(c, ch); if (!lower) return NULL; } /* If there's an upper bound it's in the second or third position. */ if (TYPE(ch) == COLON) { if (NCH(n) > 1) { node *n2 = CHILD(n, 1); if (TYPE(n2) == test) { upper = ast_for_expr(c, n2); if (!upper) return NULL; } } } else if (NCH(n) > 2) { node *n2 = CHILD(n, 2); if (TYPE(n2) == test) { upper = ast_for_expr(c, n2); if (!upper) return NULL; } } ch = CHILD(n, NCH(n) - 1); if (TYPE(ch) == sliceop) { if (NCH(ch) != 1) { ch = CHILD(ch, 1); if (TYPE(ch) == test) { step = ast_for_expr(c, ch); if (!step) return NULL; } } } return Slice(lower, upper, step, c->c_arena); } static expr_ty ast_for_binop(struct compiling *c, const node *n) { /* Must account for a sequence of expressions. How should A op B op C by represented? BinOp(BinOp(A, op, B), op, C). */ int i, nops; expr_ty expr1, expr2, result; operator_ty newoperator; expr1 = ast_for_expr(c, CHILD(n, 0)); if (!expr1) return NULL; expr2 = ast_for_expr(c, CHILD(n, 2)); if (!expr2) return NULL; newoperator = get_operator(CHILD(n, 1)); if (!newoperator) return NULL; result = BinOp(expr1, newoperator, expr2, LINENO(n), n->n_col_offset, c->c_arena); if (!result) return NULL; nops = (NCH(n) - 1) / 2; for (i = 1; i < nops; i++) { expr_ty tmp_result, tmp; const node* next_oper = CHILD(n, i * 2 + 1); newoperator = get_operator(next_oper); if (!newoperator) return NULL; tmp = ast_for_expr(c, CHILD(n, i * 2 + 2)); if (!tmp) return NULL; tmp_result = BinOp(result, newoperator, tmp, LINENO(next_oper), next_oper->n_col_offset, c->c_arena); if (!tmp_result) return NULL; result = tmp_result; } return result; } static expr_ty ast_for_trailer(struct compiling *c, const node *n, expr_ty left_expr) { /* trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME subscriptlist: subscript (',' subscript)* [','] subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop] */ REQ(n, trailer); if (TYPE(CHILD(n, 0)) == LPAR) { if (NCH(n) == 2) return Call(left_expr, NULL, NULL, NULL, NULL, LINENO(n), n->n_col_offset, c->c_arena); else return ast_for_call(c, CHILD(n, 1), left_expr); } else if (TYPE(CHILD(n, 0)) == DOT ) { PyObject *attr_id = NEW_IDENTIFIER(CHILD(n, 1)); if (!attr_id) return NULL; return Attribute(left_expr, attr_id, Load, LINENO(n), n->n_col_offset, c->c_arena); } else { REQ(CHILD(n, 0), LSQB); REQ(CHILD(n, 2), RSQB); n = CHILD(n, 1); if (NCH(n) == 1) { slice_ty slc = ast_for_slice(c, CHILD(n, 0)); if (!slc) return NULL; return Subscript(left_expr, slc, Load, LINENO(n), n->n_col_offset, c->c_arena); } else { /* The grammar is ambiguous here. The ambiguity is resolved by treating the sequence as a tuple literal if there are no slice features. */ int j; slice_ty slc; expr_ty e; bool simple = true; asdl_seq *slices, *elts; slices = asdl_seq_new((NCH(n) + 1) / 2, c->c_arena); if (!slices) return NULL; for (j = 0; j < NCH(n); j += 2) { slc = ast_for_slice(c, CHILD(n, j)); if (!slc) return NULL; if (slc->kind != Index_kind) simple = false; asdl_seq_SET(slices, j / 2, slc); } if (!simple) { return Subscript(left_expr, ExtSlice(slices, c->c_arena), Load, LINENO(n), n->n_col_offset, c->c_arena); } /* extract Index values and put them in a Tuple */ elts = asdl_seq_new(asdl_seq_LEN(slices), c->c_arena); if (!elts) return NULL; for (j = 0; j < asdl_seq_LEN(slices); ++j) { slc = (slice_ty)asdl_seq_GET(slices, j); assert(slc->kind == Index_kind && slc->v.Index.value); asdl_seq_SET(elts, j, slc->v.Index.value); } e = Tuple(elts, Load, LINENO(n), n->n_col_offset, c->c_arena); if (!e) return NULL; return Subscript(left_expr, Index(e, c->c_arena), Load, LINENO(n), n->n_col_offset, c->c_arena); } } } static expr_ty ast_for_factor(struct compiling *c, const node *n) { node *pfactor, *ppower, *patom, *pnum; expr_ty expression; /* If the unary - operator is applied to a constant, don't generate a UNARY_NEGATIVE opcode. Just store the approriate value as a constant. The peephole optimizer already does something like this but it doesn't handle the case where the constant is (sys.maxint - 1). In that case, we want a PyIntObject, not a PyLongObject. */ if (TYPE(CHILD(n, 0)) == MINUS && NCH(n) == 2 && TYPE((pfactor = CHILD(n, 1))) == factor && NCH(pfactor) == 1 && TYPE((ppower = CHILD(pfactor, 0))) == power && NCH(ppower) == 1 && TYPE((patom = CHILD(ppower, 0))) == atom && TYPE((pnum = CHILD(patom, 0))) == NUMBER) { char *s = PyObject_MALLOC(strlen(STR(pnum)) + 2); if (s == NULL) return NULL; s[0] = '-'; strcpy(s + 1, STR(pnum)); PyObject_FREE(STR(pnum)); STR(pnum) = s; return ast_for_atom(c, patom); } expression = ast_for_expr(c, CHILD(n, 1)); if (!expression) return NULL; switch (TYPE(CHILD(n, 0))) { case PLUS: return UnaryOp(UAdd, expression, LINENO(n), n->n_col_offset, c->c_arena); case MINUS: return UnaryOp(USub, expression, LINENO(n), n->n_col_offset, c->c_arena); case TILDE: return UnaryOp(Invert, expression, LINENO(n), n->n_col_offset, c->c_arena); } PyErr_Format(PyExc_SystemError, "unhandled factor: %d", TYPE(CHILD(n, 0))); return NULL; } static expr_ty ast_for_power(struct compiling *c, const node *n) { /* power: atom trailer* ('**' factor)* */ int i; expr_ty e, tmp; REQ(n, power); e = ast_for_atom(c, CHILD(n, 0)); if (!e) return NULL; if (NCH(n) == 1) return e; for (i = 1; i < NCH(n); i++) { node *ch = CHILD(n, i); if (TYPE(ch) != trailer) break; tmp = ast_for_trailer(c, ch, e); if (!tmp) return NULL; tmp->lineno = e->lineno; tmp->col_offset = e->col_offset; e = tmp; } if (TYPE(CHILD(n, NCH(n) - 1)) == factor) { expr_ty f = ast_for_expr(c, CHILD(n, NCH(n) - 1)); if (!f) return NULL; tmp = BinOp(e, Pow, f, LINENO(n), n->n_col_offset, c->c_arena); if (!tmp) return NULL; e = tmp; } return e; } /* Do not name a variable 'expr'! Will cause a compile error. */ static expr_ty ast_for_expr(struct compiling *c, const node *n) { /* handle the full range of simple expressions test: or_test ['if' or_test 'else' test] | lambdef or_test: and_test ('or' and_test)* and_test: not_test ('and' not_test)* not_test: 'not' not_test | comparison comparison: expr (comp_op expr)* expr: xor_expr ('|' xor_expr)* xor_expr: and_expr ('^' and_expr)* and_expr: shift_expr ('&' shift_expr)* shift_expr: arith_expr (('<<'|'>>') arith_expr)* arith_expr: term (('+'|'-') term)* term: factor (('*'|'/'|'%'|'//') factor)* factor: ('+'|'-'|'~') factor | power power: atom trailer* ('**' factor)* As well as modified versions that exist for backward compatibility, to explicitly allow: [ x for x in lambda: 0, lambda: 1 ] (which would be ambiguous without these extra rules) old_test: or_test | old_lambdef old_lambdef: 'lambda' [vararglist] ':' old_test */ asdl_seq *seq; int i; loop: switch (TYPE(n)) { case test: case old_test: if (TYPE(CHILD(n, 0)) == lambdef || TYPE(CHILD(n, 0)) == old_lambdef) return ast_for_lambdef(c, CHILD(n, 0)); else if (NCH(n) > 1) return ast_for_ifexpr(c, n); /* Fallthrough */ case or_test: case and_test: if (NCH(n) == 1) { n = CHILD(n, 0); goto loop; } seq = asdl_seq_new((NCH(n) + 1) / 2, c->c_arena); if (!seq) return NULL; for (i = 0; i < NCH(n); i += 2) { expr_ty e = ast_for_expr(c, CHILD(n, i)); if (!e) return NULL; asdl_seq_SET(seq, i / 2, e); } if (!strcmp(STR(CHILD(n, 1)), "and")) return BoolOp(And, seq, LINENO(n), n->n_col_offset, c->c_arena); assert(!strcmp(STR(CHILD(n, 1)), "or")); return BoolOp(Or, seq, LINENO(n), n->n_col_offset, c->c_arena); case not_test: if (NCH(n) == 1) { n = CHILD(n, 0); goto loop; } else { expr_ty expression = ast_for_expr(c, CHILD(n, 1)); if (!expression) return NULL; return UnaryOp(Not, expression, LINENO(n), n->n_col_offset, c->c_arena); } case comparison: if (NCH(n) == 1) { n = CHILD(n, 0); goto loop; } else { expr_ty expression; asdl_int_seq *ops; asdl_seq *cmps; ops = asdl_int_seq_new(NCH(n) / 2, c->c_arena); if (!ops) return NULL; cmps = asdl_seq_new(NCH(n) / 2, c->c_arena); if (!cmps) { return NULL; } for (i = 1; i < NCH(n); i += 2) { cmpop_ty newoperator; newoperator = ast_for_comp_op(c, CHILD(n, i)); if (!newoperator) { return NULL; } expression = ast_for_expr(c, CHILD(n, i + 1)); if (!expression) { return NULL; } asdl_seq_SET(ops, i / 2, newoperator); asdl_seq_SET(cmps, i / 2, expression); } expression = ast_for_expr(c, CHILD(n, 0)); if (!expression) { return NULL; } return Compare(expression, ops, cmps, LINENO(n), n->n_col_offset, c->c_arena); } break; /* The next five cases all handle BinOps. The main body of code is the same in each case, but the switch turned inside out to reuse the code for each type of operator. */ case expr: case xor_expr: case and_expr: case shift_expr: case arith_expr: case term: if (NCH(n) == 1) { n = CHILD(n, 0); goto loop; } return ast_for_binop(c, n); case yield_expr: { expr_ty exp = NULL; if (NCH(n) == 2) { exp = ast_for_testlist(c, CHILD(n, 1)); if (!exp) return NULL; } return Yield(exp, LINENO(n), n->n_col_offset, c->c_arena); } case factor: if (NCH(n) == 1) { n = CHILD(n, 0); goto loop; } return ast_for_factor(c, n); case power: return ast_for_power(c, n); default: PyErr_Format(PyExc_SystemError, "unhandled expr: %d", TYPE(n)); return NULL; } /* should never get here unless if error is set */ return NULL; } static expr_ty ast_for_call(struct compiling *c, const node *n, expr_ty func) { /* arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test) argument: [test '='] test [gen_for] # Really [keyword '='] test */ int i, nargs, nkeywords, ngens; asdl_seq *args; asdl_seq *keywords; expr_ty vararg = NULL, kwarg = NULL; REQ(n, arglist); nargs = 0; nkeywords = 0; ngens = 0; for (i = 0; i < NCH(n); i++) { node *ch = CHILD(n, i); if (TYPE(ch) == argument) { if (NCH(ch) == 1) nargs++; else if (TYPE(CHILD(ch, 1)) == gen_for) ngens++; else nkeywords++; } } if (ngens > 1 || (ngens && (nargs || nkeywords))) { ast_error(n, "Generator expression must be parenthesized " "if not sole argument"); return NULL; } if (nargs + nkeywords + ngens > 255) { ast_error(n, "more than 255 arguments"); return NULL; } args = asdl_seq_new(nargs + ngens, c->c_arena); if (!args) return NULL; keywords = asdl_seq_new(nkeywords, c->c_arena); if (!keywords) return NULL; nargs = 0; nkeywords = 0; for (i = 0; i < NCH(n); i++) { node *ch = CHILD(n, i); if (TYPE(ch) == argument) { expr_ty e; if (NCH(ch) == 1) { if (nkeywords) { ast_error(CHILD(ch, 0), "non-keyword arg after keyword arg"); return NULL; } if (vararg) { ast_error(CHILD(ch, 0), "only named arguments may follow *expression"); return NULL; } e = ast_for_expr(c, CHILD(ch, 0)); if (!e) return NULL; asdl_seq_SET(args, nargs++, e); } else if (TYPE(CHILD(ch, 1)) == gen_for) { e = ast_for_genexp(c, ch); if (!e) return NULL; asdl_seq_SET(args, nargs++, e); } else { keyword_ty kw; identifier key; int k; char *tmp; /* CHILD(ch, 0) is test, but must be an identifier? */ e = ast_for_expr(c, CHILD(ch, 0)); if (!e) return NULL; /* f(lambda x: x[0] = 3) ends up getting parsed with * LHS test = lambda x: x[0], and RHS test = 3. * SF bug 132313 points out that complaining about a keyword * then is very confusing. */ if (e->kind == Lambda_kind) { ast_error(CHILD(ch, 0), "lambda cannot contain assignment"); return NULL; } else if (e->kind != Name_kind) { ast_error(CHILD(ch, 0), "keyword can't be an expression"); return NULL; } key = e->v.Name.id; if (!forbidden_check(c, CHILD(ch, 0), PyBytes_AS_STRING(key))) return NULL; for (k = 0; k < nkeywords; k++) { tmp = PyString_AS_STRING( ((keyword_ty)asdl_seq_GET(keywords, k))->arg); if (!strcmp(tmp, PyString_AS_STRING(key))) { ast_error(CHILD(ch, 0), "keyword argument repeated"); return NULL; } } e = ast_for_expr(c, CHILD(ch, 2)); if (!e) return NULL; kw = keyword(key, e, c->c_arena); if (!kw) return NULL; asdl_seq_SET(keywords, nkeywords++, kw); } } else if (TYPE(ch) == STAR) { vararg = ast_for_expr(c, CHILD(n, i+1)); if (!vararg) return NULL; i++; } else if (TYPE(ch) == DOUBLESTAR) { kwarg = ast_for_expr(c, CHILD(n, i+1)); if (!kwarg) return NULL; i++; } } return Call(func, args, keywords, vararg, kwarg, func->lineno, func->col_offset, c->c_arena); } static expr_ty ast_for_testlist(struct compiling *c, const node* n) { /* testlist_gexp: test (',' test)* [','] */ /* testlist: test (',' test)* [','] */ /* testlist_safe: test (',' test)+ [','] */ /* testlist1: test (',' test)* */ assert(NCH(n) > 0); if (TYPE(n) == testlist_gexp) { if (NCH(n) > 1) assert(TYPE(CHILD(n, 1)) != gen_for); } else { assert(TYPE(n) == testlist || TYPE(n) == testlist_safe || TYPE(n) == testlist1); } if (NCH(n) == 1) return ast_for_expr(c, CHILD(n, 0)); else { asdl_seq *tmp = seq_for_testlist(c, n); if (!tmp) return NULL; return Tuple(tmp, Load, LINENO(n), n->n_col_offset, c->c_arena); } } static expr_ty ast_for_testlist_gexp(struct compiling *c, const node* n) { /* testlist_gexp: test ( gen_for | (',' test)* [','] ) */ /* argument: test [ gen_for ] */ assert(TYPE(n) == testlist_gexp || TYPE(n) == argument); if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == gen_for) return ast_for_genexp(c, n); return ast_for_testlist(c, n); } /* like ast_for_testlist() but returns a sequence */ static asdl_seq* ast_for_class_bases(struct compiling *c, const node* n) { /* testlist: test (',' test)* [','] */ assert(NCH(n) > 0); REQ(n, testlist); if (NCH(n) == 1) { expr_ty base; asdl_seq *bases = asdl_seq_new(1, c->c_arena); if (!bases) return NULL; base = ast_for_expr(c, CHILD(n, 0)); if (!base) return NULL; asdl_seq_SET(bases, 0, base); return bases; } return seq_for_testlist(c, n); } static stmt_ty ast_for_expr_stmt(struct compiling *c, const node *n) { REQ(n, expr_stmt); /* expr_stmt: testlist (augassign (yield_expr|testlist) | ('=' (yield_expr|testlist))*) testlist: test (',' test)* [','] augassign: '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=' test: ... here starts the operator precendence dance */ if (NCH(n) == 1) { expr_ty e = ast_for_testlist(c, CHILD(n, 0)); if (!e) return NULL; return Expr(e, LINENO(n), n->n_col_offset, c->c_arena); } else if (TYPE(CHILD(n, 1)) == augassign) { expr_ty expr1, expr2; operator_ty newoperator; node *ch = CHILD(n, 0); expr1 = ast_for_testlist(c, ch); if (!expr1) return NULL; if(!set_context(c, expr1, Store, ch)) return NULL; ch = CHILD(n, 2); if (TYPE(ch) == testlist) expr2 = ast_for_testlist(c, ch); else expr2 = ast_for_expr(c, ch); if (!expr2) return NULL; newoperator = ast_for_augassign(c, CHILD(n, 1)); if (!newoperator) return NULL; return AugAssign(expr1, newoperator, expr2, LINENO(n), n->n_col_offset, c->c_arena); } else { int i; asdl_seq *targets; node *value; expr_ty expression; /* a normal assignment */ REQ(CHILD(n, 1), EQUAL); targets = asdl_seq_new(NCH(n) / 2, c->c_arena); if (!targets) return NULL; for (i = 0; i < NCH(n) - 2; i += 2) { expr_ty e; node *ch = CHILD(n, i); if (TYPE(ch) == yield_expr) { ast_error(ch, "assignment to yield expression not possible"); return NULL; } e = ast_for_testlist(c, ch); /* set context to assign */ if (!e) return NULL; if (!set_context(c, e, Store, CHILD(n, i))) return NULL; asdl_seq_SET(targets, i / 2, e); } value = CHILD(n, NCH(n) - 1); if (TYPE(value) == testlist) expression = ast_for_testlist(c, value); else expression = ast_for_expr(c, value); if (!expression) return NULL; return Assign(targets, expression, LINENO(n), n->n_col_offset, c->c_arena); } } static stmt_ty ast_for_print_stmt(struct compiling *c, const node *n) { /* print_stmt: 'print' ( [ test (',' test)* [','] ] | '>>' test [ (',' test)+ [','] ] ) */ expr_ty dest = NULL, expression; asdl_seq *seq = NULL; bool nl; int i, j, values_count, start = 1; REQ(n, print_stmt); if (NCH(n) >= 2 && TYPE(CHILD(n, 1)) == RIGHTSHIFT) { dest = ast_for_expr(c, CHILD(n, 2)); if (!dest) return NULL; start = 4; } values_count = (NCH(n) + 1 - start) / 2; if (values_count) { seq = asdl_seq_new(values_count, c->c_arena); if (!seq) return NULL; for (i = start, j = 0; i < NCH(n); i += 2, ++j) { expression = ast_for_expr(c, CHILD(n, i)); if (!expression) return NULL; asdl_seq_SET(seq, j, expression); } } nl = (TYPE(CHILD(n, NCH(n) - 1)) == COMMA) ? false : true; return Print(dest, seq, nl, LINENO(n), n->n_col_offset, c->c_arena); } static asdl_seq * ast_for_exprlist(struct compiling *c, const node *n, expr_context_ty context) { asdl_seq *seq; int i; expr_ty e; REQ(n, exprlist); seq = asdl_seq_new((NCH(n) + 1) / 2, c->c_arena); if (!seq) return NULL; for (i = 0; i < NCH(n); i += 2) { e = ast_for_expr(c, CHILD(n, i)); if (!e) return NULL; asdl_seq_SET(seq, i / 2, e); if (context && !set_context(c, e, context, CHILD(n, i))) return NULL; } return seq; } static stmt_ty ast_for_del_stmt(struct compiling *c, const node *n) { asdl_seq *expr_list; /* del_stmt: 'del' exprlist */ REQ(n, del_stmt); expr_list = ast_for_exprlist(c, CHILD(n, 1), Del); if (!expr_list) return NULL; return Delete(expr_list, LINENO(n), n->n_col_offset, c->c_arena); } static stmt_ty ast_for_flow_stmt(struct compiling *c, const node *n) { /* flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt break_stmt: 'break' continue_stmt: 'continue' return_stmt: 'return' [testlist] yield_stmt: yield_expr yield_expr: 'yield' testlist raise_stmt: 'raise' [test [',' test [',' test]]] */ node *ch; REQ(n, flow_stmt); ch = CHILD(n, 0); switch (TYPE(ch)) { case break_stmt: return Break(LINENO(n), n->n_col_offset, c->c_arena); case continue_stmt: return Continue(LINENO(n), n->n_col_offset, c->c_arena); case yield_stmt: { /* will reduce to yield_expr */ expr_ty exp = ast_for_expr(c, CHILD(ch, 0)); if (!exp) return NULL; return Expr(exp, LINENO(n), n->n_col_offset, c->c_arena); } case return_stmt: if (NCH(ch) == 1) return Return(NULL, LINENO(n), n->n_col_offset, c->c_arena); else { expr_ty expression = ast_for_testlist(c, CHILD(ch, 1)); if (!expression) return NULL; return Return(expression, LINENO(n), n->n_col_offset, c->c_arena); } case raise_stmt: if (NCH(ch) == 1) return Raise(NULL, NULL, NULL, LINENO(n), n->n_col_offset, c->c_arena); else if (NCH(ch) == 2) { expr_ty expression = ast_for_expr(c, CHILD(ch, 1)); if (!expression) return NULL; return Raise(expression, NULL, NULL, LINENO(n), n->n_col_offset, c->c_arena); } else if (NCH(ch) == 4) { expr_ty expr1, expr2; expr1 = ast_for_expr(c, CHILD(ch, 1)); if (!expr1) return NULL; expr2 = ast_for_expr(c, CHILD(ch, 3)); if (!expr2) return NULL; return Raise(expr1, expr2, NULL, LINENO(n), n->n_col_offset, c->c_arena); } else if (NCH(ch) == 6) { expr_ty expr1, expr2, expr3; expr1 = ast_for_expr(c, CHILD(ch, 1)); if (!expr1) return NULL; expr2 = ast_for_expr(c, CHILD(ch, 3)); if (!expr2) return NULL; expr3 = ast_for_expr(c, CHILD(ch, 5)); if (!expr3) return NULL; return Raise(expr1, expr2, expr3, LINENO(n), n->n_col_offset, c->c_arena); } default: PyErr_Format(PyExc_SystemError, "unexpected flow_stmt: %d", TYPE(ch)); return NULL; } PyErr_SetString(PyExc_SystemError, "unhandled flow statement"); return NULL; } static alias_ty alias_for_import_name(struct compiling *c, const node *n, int store) { /* import_as_name: NAME ['as' NAME] dotted_as_name: dotted_name ['as' NAME] dotted_name: NAME ('.' NAME)* */ PyObject *str, *name; loop: switch (TYPE(n)) { case import_as_name: { node *name_node = CHILD(n, 0); str = NULL; if (NCH(n) == 3) { node *str_node = CHILD(n, 2); if (store && !forbidden_check(c, str_node, STR(str_node))) return NULL; str = NEW_IDENTIFIER(str_node); if (!str) return NULL; } else { if (!forbidden_check(c, name_node, STR(name_node))) return NULL; } name = NEW_IDENTIFIER(name_node); if (!name) return NULL; return alias(name, str, c->c_arena); } case dotted_as_name: if (NCH(n) == 1) { n = CHILD(n, 0); goto loop; } else { node *asname_node = CHILD(n, 2); alias_ty a = alias_for_import_name(c, CHILD(n, 0), 0); if (!a) return NULL; assert(!a->asname); if (!forbidden_check(c, asname_node, STR(asname_node))) return NULL; a->asname = NEW_IDENTIFIER(asname_node); if (!a->asname) return NULL; return a; } break; case dotted_name: if (NCH(n) == 1) { node *name_node = CHILD(n, 0); if (store && !forbidden_check(c, name_node, STR(name_node))) return NULL; name = NEW_IDENTIFIER(name_node); if (!name) return NULL; return alias(name, NULL, c->c_arena); } else { /* Create a string of the form "a.b.c" */ int i; size_t len; char *s; len = 0; for (i = 0; i < NCH(n); i += 2) /* length of string plus one for the dot */ len += strlen(STR(CHILD(n, i))) + 1; len--; /* the last name doesn't have a dot */ str = PyString_FromStringAndSize(NULL, len); if (!str) return NULL; s = PyString_AS_STRING(str); if (!s) return NULL; for (i = 0; i < NCH(n); i += 2) { char *sch = STR(CHILD(n, i)); strcpy(s, STR(CHILD(n, i))); s += strlen(sch); *s++ = '.'; } --s; *s = '\0'; PyString_InternInPlace(&str); PyArena_AddPyObject(c->c_arena, str); return alias(str, NULL, c->c_arena); } break; case STAR: str = PyString_InternFromString("*"); PyArena_AddPyObject(c->c_arena, str); return alias(str, NULL, c->c_arena); default: PyErr_Format(PyExc_SystemError, "unexpected import name: %d", TYPE(n)); return NULL; } PyErr_SetString(PyExc_SystemError, "unhandled import name condition"); return NULL; } static stmt_ty ast_for_import_stmt(struct compiling *c, const node *n) { /* import_stmt: import_name | import_from import_name: 'import' dotted_as_names import_from: 'from' ('.'* dotted_name | '.') 'import' ('*' | '(' import_as_names ')' | import_as_names) */ int lineno; int col_offset; int i; asdl_seq *aliases; REQ(n, import_stmt); lineno = LINENO(n); col_offset = n->n_col_offset; n = CHILD(n, 0); if (TYPE(n) == import_name) { n = CHILD(n, 1); REQ(n, dotted_as_names); aliases = asdl_seq_new((NCH(n) + 1) / 2, c->c_arena); if (!aliases) return NULL; for (i = 0; i < NCH(n); i += 2) { alias_ty import_alias = alias_for_import_name(c, CHILD(n, i), 1); if (!import_alias) return NULL; asdl_seq_SE tb_getsetters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ tb_new, /* tp_new */ }; PyObject* _PyTraceBack_FromFrame(PyObject *tb_next, PyFrameObject *frame) { assert(tb_next == NULL || PyTraceBack_Check(tb_next)); assert(frame != NULL); int addr = _PyInterpreterFrame_LASTI(frame->f_frame) * sizeof(_Py_CODEUNIT); return tb_create_raw((PyTracebackObject *)tb_next, frame, addr, PyFrame_GetLineNumber(frame)); } int PyTraceBack_Here(PyFrameObject *frame) { PyObject *exc = PyErr_GetRaisedException(); assert(PyExceptionInstance_Check(exc)); PyObject *tb = PyException_GetTraceback(exc); PyObject *newtb = _PyTraceBack_FromFrame(tb, frame); Py_XDECREF(tb); if (newtb == NULL) { _PyErr_ChainExceptions1(exc); return -1; } PyException_SetTraceback(exc, newtb); Py_XDECREF(newtb); PyErr_SetRaisedException(exc); return 0; } /* Insert a frame into the traceback for (funcname, filename, lineno). */ void _PyTraceback_Add(const char *funcname, const char *filename, int lineno) { PyObject *globals; PyCodeObject *code; PyFrameObject *frame; PyThreadState *tstate = _PyThreadState_GET(); /* Save and clear the current exception. Python functions must not be called with an exception set. Calling Python functions happens when the codec of the filesystem encoding is implemented in pure Python. */ PyObject *exc = _PyErr_GetRaisedException(tstate); globals = PyDict_New(); if (!globals) goto error; code = PyCode_NewEmpty(filename, funcname, lineno); if (!code) { Py_DECREF(globals); goto error; } frame = PyFrame_New(tstate, code, globals, NULL); Py_DECREF(globals); Py_DECREF(code); if (!frame) goto error; frame->f_lineno = lineno; _PyErr_SetRaisedException(tstate, exc); PyTraceBack_Here(frame); Py_DECREF(frame); return; error: _PyErr_ChainExceptions1(exc); } static PyObject * _Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject *io) { Py_ssize_t i; PyObject *binary; PyObject *v; Py_ssize_t npath; size_t taillen; PyObject *syspath; PyObject *path; const char* tail; PyObject *filebytes; const char* filepath; Py_ssize_t len; PyObject* result; PyObject *open = NULL; filebytes = PyUnicode_EncodeFSDefault(filename); if (filebytes == NULL) { PyErr_Clear(); return NULL; } filepath = PyBytes_AS_STRING(filebytes); /* Search tail of filename in sys.path before giving up */ tail = strrchr(filepath, SEP); if (tail == NULL) tail = filepath; else tail++; taillen = strlen(tail); PyThreadState *tstate = _PyThreadState_GET(); syspath = _PySys_GetAttr(tstate, &_Py_ID(path)); if (syspath == NULL || !PyList_Check(syspath)) goto error; npath = PyList_Size(syspath); open = PyObject_GetAttr(io, &_Py_ID(open)); for (i = 0; i < npath; i++) { v = PyList_GetItem(syspath, i); if (v == NULL) { PyErr_Clear(); break; } if (!PyUnicode_Check(v)) continue; path = PyUnicode_EncodeFSDefault(v); if (path == NULL) { PyErr_Clear(); continue; } len = PyBytes_GET_SIZE(path); if (len + 1 + (Py_ssize_t)taillen >= (Py_ssize_t)namelen - 1) { Py_DECREF(path); continue; /* Too long */ } strcpy(namebuf, PyBytes_AS_STRING(path)); Py_DECREF(path); if (strlen(namebuf) != (size_t)len) continue; /* v contains '\0' */ if (len > 0 && namebuf[len-1] != SEP) namebuf[len++] = SEP; strcpy(namebuf+len, tail); binary = _PyObject_CallMethodFormat(tstate, open, "ss", namebuf, "rb"); if (binary != NULL) { result = binary; goto finally; } PyErr_Clear(); } goto error; error: result = NULL; finally: Py_XDECREF(open); Py_DECREF(filebytes); return result; } /* Writes indent spaces. Returns 0 on success and non-zero on failure. */ int _Py_WriteIndent(int indent, PyObject *f) { char buf[11] = " "; assert(strlen(buf) == 10); while (indent > 0) { if (indent < 10) { buf[indent] = '\0'; } if (PyFile_WriteString(buf, f) < 0) { return -1; } indent -= 10; } return 0; } /* Writes indent spaces, followed by the margin if it is not `\0`. Returns 0 on success and non-zero on failure. */ int _Py_WriteIndentedMargin(int indent, const char *margin, PyObject *f) { if (_Py_WriteIndent(indent, f) < 0) { return -1; } if (margin) { if (PyFile_WriteString(margin, f) < 0) { return -1; } } return 0; } static int display_source_line_with_margin(PyObject *f, PyObject *filename, int lineno, int indent, int margin_indent, const char *margin, int *truncation, PyObject **line) { int fd; int i; char *found_encoding; const char *encoding; PyObject *io; PyObject *binary; PyObject *fob = NULL; PyObject *lineobj = NULL; PyObject *res; char buf[MAXPATHLEN+1]; int kind; const void *data; /* open the file */ if (filename == NULL) return 0; /* Do not attempt to open things like <string> or <stdin> */ assert(PyUnicode_Check(filename)); if (PyUnicode_READ_CHAR(filename, 0) == '<') { Py_ssize_t len = PyUnicode_GET_LENGTH(filename); if (len > 0 && PyUnicode_READ_CHAR(filename, len - 1) == '>') { return 0; } } io = PyImport_ImportModule("io"); if (io == NULL) { return -1; } binary = _PyObject_CallMethod(io, &_Py_ID(open), "Os", filename, "rb"); if (binary == NULL) { PyErr_Clear(); binary = _Py_FindSourceFile(filename, buf, sizeof(buf), io); if (binary == NULL) { Py_DECREF(io); return -1; } } /* use the right encoding to decode the file as unicode */ fd = PyObject_AsFileDescriptor(binary); if (fd < 0) { Py_DECREF(io); Py_DECREF(binary); return 0; } found_encoding = _PyTokenizer_FindEncodingFilename(fd, filename); if (found_encoding == NULL) PyErr_Clear(); encoding = (found_encoding != NULL) ? found_encoding : "utf-8"; /* Reset position */ if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { Py_DECREF(io); Py_DECREF(binary); PyMem_Free(found_encoding); return 0; } fob = _PyObject_CallMethod(io, &_Py_ID(TextIOWrapper), "Os", binary, encoding); Py_DECREF(io); PyMem_Free(found_encoding); if (fob == NULL) { PyErr_Clear(); res = PyObject_CallMethodNoArgs(binary, &_Py_ID(close)); Py_DECREF(binary); if (res) Py_DECREF(res); else PyErr_Clear(); return 0; } Py_DECREF(binary); /* get the line number lineno */ for (i = 0; i < lineno; i++) { Py_XDECREF(lineobj); lineobj = PyFile_GetLine(fob, -1); if (!lineobj) { PyErr_Clear(); break; } } res = PyObject_CallMethodNoArgs(fob, &_Py_ID(close)); if (res) { Py_DECREF(res); } else { PyErr_Clear(); } Py_DECREF(fob); if (!lineobj || !PyUnicode_Check(lineobj)) { Py_XDECREF(lineobj); return -1; } if (line) { *line = Py_NewRef(lineobj); } /* remove the indentation of the line */ kind = PyUnicode_KIND(lineobj); data = PyUnicode_DATA(lineobj); for (i=0; i < PyUnicode_GET_LENGTH(lineobj); i++) { Py_UCS4 ch = PyUnicode_READ(kind, data, i); if (ch != ' ' && ch != '\t' && ch != '\014') break; } if (i) { PyObject *truncated; truncated = PyUnicode_Substring(lineobj, i, PyUnicode_GET_LENGTH(lineobj)); if (truncated) { Py_SETREF(lineobj, truncated); } else { PyErr_Clear(); } } if (truncation != NULL) { *truncation = i - indent; } if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) { goto error; } /* Write some spaces before the line */ if (_Py_WriteIndent(indent, f) < 0) { goto error; } /* finally display the line */ if (PyFile_WriteObject(lineobj, f, Py_PRINT_RAW) < 0) { goto error; } if (PyFile_WriteString("\n", f) < 0) { goto error; } Py_DECREF(lineobj); return 0; error: Py_DECREF(lineobj); return -1; } int _Py_DisplaySourceLine(PyObject *f, PyObject *filename, int lineno, int indent, int *truncation, PyObject **line) { return display_source_line_with_margin(f, filename, lineno, indent, 0, NULL, truncation, line); } /* AST based Traceback Specialization * * When displaying a new traceback line, for certain syntactical constructs * (e.g a subscript, an arithmetic operation) we try to create a representation * that separates the primary source of error from the rest. * * Example specialization of BinOp nodes: * Traceback (most recent call last): * File "/home/isidentical/cpython/cpython/t.py", line 10, in <module> * add_values(1, 2, 'x', 3, 4) * File "/home/isidentical/cpython/cpython/t.py", line 2, in add_values * return a + b + c + d + e * ~~~~~~^~~ * TypeError: 'NoneType' object is not subscriptable */ #define IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\f')) static int extract_anchors_from_expr(const char *segment_str, expr_ty expr, Py_ssize_t *left_anchor, Py_ssize_t *right_anchor, char** primary_error_char, char** secondary_error_char) { switch (expr->kind) { case BinOp_kind: { expr_ty left = expr->v.BinOp.left; expr_ty right = expr->v.BinOp.right; for (int i = left->end_col_offset; i < right->col_offset; i++) { if (IS_WHITESPACE(segment_str[i])) { continue; } *left_anchor = i; *right_anchor = i + 1; // Check whether if this a two-character operator (e.g //) if (i + 1 < right->col_offset && !IS_WHITESPACE(segment_str[i + 1])) { ++*right_anchor; } // Set the error characters *primary_error_char = "~"; *secondary_error_char = "^"; break; } return 1; } case Subscript_kind: { *left_anchor = expr->v.Subscript.value->end_col_offset; *right_anchor = expr->v.Subscript.slice->end_col_offset + 1; // Set the error characters *primary_error_char = "~"; *secondary_error_char = "^"; return 1; } default: return 0; } } static int extract_anchors_from_stmt(const char *segment_str, stmt_ty statement, Py_ssize_t *left_anchor, Py_ssize_t *right_anchor, char** primary_error_char, char** secondary_error_char) { switch (statement->kind) { case Expr_kind: { return extract_anchors_from_expr(segment_str, statement->v.Expr.value, left_anchor, right_anchor, primary_error_char, secondary_error_char); } default: return 0; } } static int extract_anchors_from_line(PyObject *filename, PyObject *line, Py_ssize_t start_offset, Py_ssize_t end_offset, Py_ssize_t *left_anchor, Py_ssize_t *right_anchor, char** primary_error_char, char** secondary_error_char) { int res = -1; PyArena *arena = NULL; PyObject *segment = PyUnicode_Substring(line, start_offset, end_offset); if (!segment) { goto done; } const char *segment_str = PyUnicode_AsUTF8(segment); if (!segment_str) { goto done; } arena = _PyArena_New(); if (!arena) { goto done; } PyCompilerFlags flags = _PyCompilerFlags_INIT; _PyASTOptimizeState state; state.optimize = _Py_GetConfig()->optimization_level; state.ff_features = 0; mod_ty module = _PyParser_ASTFromString(segment_str, filename, Py_file_input, &flags, arena); if (!module) { goto done; } if (!_PyAST_Optimize(module, arena, &state)) { goto done; } assert(module->kind == Module_kind); if (asdl_seq_LEN(module->v.Module.body) == 1) { stmt_ty statement = asdl_seq_GET(module->v.Module.body, 0); res = extract_anchors_from_stmt(segment_str, statement, left_anchor, right_anchor, primary_error_char, secondary_error_char); } else { res = 0; } done: if (res > 0) { // Normalize the AST offsets to byte offsets and adjust them with the // start of the actual line (instead of the source code segment). assert(segment != NULL); assert(*left_anchor >= 0); assert(*right_anchor >= 0); *left_anchor = _PyPegen_byte_offset_to_character_offset(segment, *left_anchor) + start_offset; *right_anchor = _PyPegen_byte_offset_to_character_offset(segment, *right_anchor) + start_offset; } Py_XDECREF(segment); if (arena) { _PyArena_Free(arena); } return res; } #define _TRACEBACK_SOURCE_LINE_INDENT 4 static inline int ignore_source_errors(void) { if (PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) { return -1; } PyErr_Clear(); } return 0; } static inline int print_error_location_carets(PyObject *f, int offset, Py_ssize_t start_offset, Py_ssize_t end_offset, Py_ssize_t right_start_offset, Py_ssize_t left_end_offset, const char *primary, const char *secondary) { int special_chars = (left_end_offset != -1 || right_start_offset != -1); const char *str; while (++offset <= end_offset) { if (offset <= start_offset) { str = " "; } else if (special_chars && left_end_offset < offset && offset <= right_start_offset) { str = secondary; } else { str = primary; } if (PyFile_WriteString(str, f) < 0) { return -1; } } if (PyFile_WriteString("\n", f) < 0) { return -1; } return 0; } static int tb_displayline(PyTracebackObject* tb, PyObject *f, PyObject *filename, int lineno, PyFrameObject *frame, PyObject *name, int margin_indent, const char *margin) { if (filename == NULL || name == NULL) { return -1; } if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) { return -1; } PyObject *line = PyUnicode_FromFormat(" File \"%U\", line %d, in %U\n", filename, lineno, name); if (line == NULL) { return -1; } int res = PyFile_WriteObject(line, f, Py_PRINT_RAW); Py_DECREF(line); if (res < 0) { return -1; } int err = 0; int truncation = _TRACEBACK_SOURCE_LINE_INDENT; PyObject* source_line = NULL; int rc = display_source_line_with_margin( f, filename, lineno, _TRACEBACK_SOURCE_LINE_INDENT, margin_indent, margin, &truncation, &source_line); if (rc != 0 || !source_line) { /* ignore errors since we can't report them, can we? */ err = ignore_source_errors(); goto done; } int code_offset = tb->tb_lasti; PyCodeObject* code = frame->f_frame->f_code; const Py_ssize_t source_line_len = PyUnicode_GET_LENGTH(source_line); int start_line; int end_line; int start_col_byte_offset; int end_col_byte_offset; if (!PyCode_Addr2Location(code, code_offset, &start_line, &start_col_byte_offset, &end_line, &end_col_byte_offset)) { goto done; } if (start_line < 0 || end_line < 0 || start_col_byte_offset < 0 || end_col_byte_offset < 0) { goto done; } // When displaying errors, we will use the following generic structure: // // ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE ERROR LINE // ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^~~~~~~~~~~~~~~~~~~~ // | |-> left_end_offset | |-> end_offset // |-> start_offset |-> right_start_offset // // In general we will only have (start_offset, end_offset) but we can gather more information // by analyzing the AST of the text between *start_offset* and *end_offset*. If this succeeds // we could get *left_end_offset* and *right_start_offset* and some selection of characters for // the different ranges (primary_error_char and secondary_error_char). If we cannot obtain the // AST information or we cannot identify special ranges within it, then left_end_offset and // right_end_offset will be set to -1. // // To keep the column indicators pertinent, they are not shown when the primary character // spans the whole line. // Convert the utf-8 byte offset to the actual character offset so we print the right number of carets. assert(source_line); Py_ssize_t start_offset = _PyPegen_byte_offset_to_character_offset(source_line, start_col_byte_offset); if (start_offset < 0) { err = ignore_source_errors() < 0; goto done; } Py_ssize_t end_offset = _PyPegen_byte_offset_to_character_offset(source_line, end_col_byte_offset); if (end_offset < 0) { err = ignore_source_errors() < 0; goto done; } Py_ssize_t left_end_offset = -1; Py_ssize_t right_start_offset = -1; char *primary_error_char = "^"; char *secondary_error_char = primary_error_char; if (start_line == end_line) { int res = extract_anchors_from_line(filename, source_line, start_offset, end_offset, &left_end_offset, &right_start_offset, &primary_error_char, &secondary_error_char); if (res < 0 && ignore_source_errors() < 0) { goto done; } } else { // If this is a multi-line expression, then we will highlight until // the last non-whitespace character. const char *source_line_str = PyUnicode_AsUTF8(source_line); if (!source_line_str) { goto done; } Py_ssize_t i = source_line_len; while (--i >= 0) { if (!IS_WHITESPACE(source_line_str[i])) { break; } } end_offset = i + 1; } // Elide indicators if primary char spans the frame line Py_ssize_t stripped_line_len = source_line_len - truncation - _TRACEBACK_SOURCE_LINE_INDENT; bool has_secondary_ranges = (left_end_offset != -1 || right_start_offset != -1); if (end_offset - start_offset == stripped_line_len && !has_secondary_ranges) { goto done; } if (_Py_WriteIndentedMargin(margin_indent, margin, f) < 0) { err = -1; goto done; } if (print_error_location_carets(f, truncation, start_offset, end_offset, right_start_offset, left_end_offset, primary_error_char, secondary_error_char) < 0) { err = -1; goto done; } done: Py_XDECREF(source_line); return err; } static const int TB_RECURSIVE_CUTOFF = 3; // Also hardcoded in traceback.py. static int tb_print_line_repeated(PyObject *f, long cnt) { cnt -= TB_RECURSIVE_CUTOFF; PyObject *line = PyUnicode_FromFormat( (cnt > 1) ? " [Previous line repeated %ld more times]\n" : " [Previous line repeated %ld more time]\n", cnt); if (line == NULL) { return -1; } int err = PyFile_WriteObject(line, f, Py_PRINT_RAW); Py_DECREF(line); return err; } static int tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit, int indent, const char *margin) { PyCodeObject *code = NULL; Py_ssize_t depth = 0; PyObject *last_file = NULL; int last_line = -1; PyObject *last_name = NULL; long cnt = 0; PyTracebackObject *tb1 = tb; while (tb1 != NULL) { depth++; tb1 = tb1->tb_next; } while (tb != NULL && depth > limit) { depth--; tb = tb->tb_next; } while (tb != NULL) { code = PyFrame_GetCode(tb->tb_frame); if (last_file == NULL || code->co_filename != last_file || last_line == -1 || tb->tb_lineno != last_line || last_name == NULL || code->co_name != last_name) { if (cnt > TB_RECURSIVE_CUTOFF) { if (tb_print_line_repeated(f, cnt) < 0) { goto error; } } last_file = code->co_filename; last_line = tb->tb_lineno; last_name = code->co_name; cnt = 0; } cnt++; if (cnt <= TB_RECURSIVE_CUTOFF) { if (tb_displayline(tb, f, code->co_filename, tb->tb_lineno, tb->tb_frame, code->co_name, indent, margin) < 0) { goto error; } if (PyErr_CheckSignals() < 0) { goto error; } } Py_CLEAR(code); tb = tb->tb_next; } if (cnt > TB_RECURSIVE_CUTOFF) { if (tb_print_line_repeated(f, cnt) < 0) { goto error; } } return 0; error: Py_XDECREF(code); return -1; } #define PyTraceBack_LIMIT 1000 int _PyTraceBack_Print_Indented(PyObject *v, int indent, const char *margin, const char *header_margin, const char *header, PyObject *f) { PyObject *limitv; long limit = PyTraceBack_LIMIT; if (v == NULL) { return 0; } if (!PyTraceBack_Check(v)) { PyErr_BadInternalCall(); return -1; } limitv = PySys_GetObject("tracebacklimit"); if (limitv && PyLong_Check(limitv)) { int overflow; limit = PyLong_AsLongAndOverflow(limitv, &overflow); if (overflow > 0) { limit = LONG_MAX; } else if (limit <= 0) { return 0; } } if (_Py_WriteIndentedMargin(indent, header_margin, f) < 0) { return -1; } if (PyFile_WriteString(header, f) < 0) { return -1; } if (tb_printinternal((PyTracebackObject *)v, f, limit, indent, margin) < 0) { return -1; } return 0; } int PyTraceBack_Print(PyObject *v, PyObject *f) { int indent = 0; const char *margin = NULL; const char *header_margin = NULL; const char *header = EXCEPTION_TB_HEADER; return _PyTraceBack_Print_Indented(v, indent, margin, header_margin, header, f); } /* Format an integer in range [0; 0xffffffff] to decimal and write it into the file fd. This function is signal safe. */ void _Py_DumpDecimal(int fd, size_t value) { /* maximum number of characters required for output of %lld or %p. We need at most ceil(log10(256)*SIZEOF_LONG_LONG) digits, plus 1 for the null byte. 53/22 is an upper bound for log10(256). */ char buffer[1 + (sizeof(size_t)*53-1) / 22 + 1]; char *ptr, *end; end = &buffer[Py_ARRAY_LENGTH(buffer) - 1]; ptr = end; *ptr = '\0'; do { --ptr; assert(ptr >= buffer); *ptr = '0' + (value % 10); value /= 10; } while (value); _Py_write_noraise(fd, ptr, end - ptr); } /* Format an integer as hexadecimal with width digits into fd file descriptor. The function is signal safe. */ void _Py_DumpHexadecimal(int fd, uintptr_t value, Py_ssize_t width) { char buffer[sizeof(uintptr_t) * 2 + 1], *ptr, *end; const Py_ssize_t size = Py_ARRAY_LENGTH(buffer) - 1; if (width > size) width = size; /* it's ok if width is negative */ end = &buffer[size]; ptr = end; *ptr = '\0'; do { --ptr; assert(ptr >= buffer); *ptr = Py_hexdigits[value & 15]; value >>= 4; } while ((end - ptr) < width || value); _Py_write_noraise(fd, ptr, end - ptr); } void _Py_DumpASCII(int fd, PyObject *text) { PyASCIIObject *ascii = _PyASCIIObject_CAST(text); Py_ssize_t i, size; int truncated; int kind; void *data = NULL; Py_UCS4 ch; if (!PyUnicode_Check(text)) return; size = ascii->length; kind = ascii->state.kind; if (ascii->state.compact) { if (ascii->state.ascii) data = ascii + 1; else data = _PyCompactUnicodeObject_CAST(text) + 1; } else { data = _PyUnicodeObject_CAST(text)->data.any; if (data == NULL) return; } if (MAX_STRING_LENGTH < size) { size = MAX_STRING_LENGTH; truncated = 1; } else { truncated = 0; } // Is an ASCII string? if (ascii->state.ascii) { assert(kind == PyUnicode_1BYTE_KIND); char *str = data; int need_escape = 0; for (i=0; i < size; i++) { ch = str[i]; if (!(' ' <= ch && ch <= 126)) { need_escape = 1; break; } } if (!need_escape) { // The string can be written with a single write() syscall _Py_write_noraise(fd, str, size); goto done; } } for (i=0; i < size; i++) { ch = PyUnicode_READ(kind, data, i); if (' ' <= ch && ch <= 126) { /* printable ASCII character */ char c = (char)ch; _Py_write_noraise(fd, &c, 1); } else if (ch <= 0xff) { PUTS(fd, "\\x"); _Py_DumpHexadecimal(fd, ch, 2); } else if (ch <= 0xffff) { PUTS(fd, "\\u"); _Py_DumpHexadecimal(fd, ch, 4); } else { PUTS(fd, "\\U"); _Py_DumpHexadecimal(fd, ch, 8); } } done: if (truncated) { PUTS(fd, "..."); } } /* Write a frame into the file fd: "File "xxx", line xxx in xxx". This function is signal safe. */ static void dump_frame(int fd, _PyInterpreterFrame *frame) { PyCodeObject *code = frame->f_code; PUTS(fd, " File "); if (code->co_filename != NULL && PyUnicode_Check(code->co_filename)) { PUTS(fd, "\""); _Py_DumpASCII(fd, code->co_filename); PUTS(fd, "\""); } else { PUTS(fd, "???"); } int lineno = _PyInterpreterFrame_GetLine(frame); PUTS(fd, ", line "); if (lineno >= 0) { _Py_DumpDecimal(fd, (size_t)lineno); } else { PUTS(fd, "???"); } PUTS(fd, " in "); if (code->co_name != NULL && PyUnicode_Check(code->co_name)) { _Py_DumpASCII(fd, code->co_name); } else { PUTS(fd, "???"); } PUTS(fd, "\n"); } static void dump_traceback(int fd, PyThreadState *tstate, int write_header) { _PyInterpreterFrame *frame; unsigned int depth; if (write_header) { PUTS(fd, "Stack (most recent call first):\n"); } frame = tstate->cframe->current_frame; if (frame == NULL) { PUTS(fd, " <no Python frame>\n"); return; } depth = 0; while (1) { if (MAX_FRAME_DEPTH <= depth) { PUTS(fd, " ...\n"); break; } dump_frame(fd, frame); frame = frame->previous; if (frame == NULL) { break; } if (frame->owner == FRAME_OWNED_BY_CSTACK) { /* Trampoline frame */ frame = frame->previous; } if (frame == NULL) { break; } /* Can't have more than one shim frame in a row */ assert(frame->owner != FRAME_OWNED_BY_CSTACK); depth++; } } /* Dump the traceback of a Python thread into fd. Use write() to write the traceback and retry if write() is interrupted by a signal (failed with EINTR), but don't call the Python signal handler. The caller is responsible to call PyErr_CheckSignals() to call Python signal handlers if signals were received. */ void _Py_DumpTraceback(int fd, PyThreadState *tstate) { dump_traceback(fd, tstate, 1); } /* Write the thread identifier into the file 'fd': "Current thread 0xHHHH:\" if is_current is true, "Thread 0xHHHH:\n" otherwise. This function is signal safe. */ static void write_thread_id(int fd, PyThreadState *tstate, int is_current) { if (is_current) PUTS(fd, "Current thread 0x"); else PUTS(fd, "Thread 0x"); _Py_DumpHexadecimal(fd, tstate->thread_id, sizeof(unsigned long) * 2); PUTS(fd, " (most recent call first):\n"); } /* Dump the traceback of all Python threads into fd. Use write() to write the traceback and retry if write() is interrupted by a signal (failed with EINTR), but don't call the Python signal handler. The caller is responsible to call PyErr_CheckSignals() to call Python signal handlers if signals were received. */ const char* _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp, PyThreadState *current_tstate) { PyThreadState *tstate; unsigned int nthreads; if (current_tstate == NULL) { /* _Py_DumpTracebackThreads() is called from signal handlers by faulthandler. SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals and are thus delivered to the thread that caused the fault. Get the Python thread state of the current thread. PyThreadState_Get() doesn't give the state of the thread that caused the fault if the thread released the GIL, and so _PyThreadState_GET() cannot be used. Read the thread specific storage (TSS) instead: call PyGILState_GetThisThreadState(). */ current_tstate = PyGILState_GetThisThreadState(); } if (interp == NULL) { if (current_tstate == NULL) { interp = _PyGILState_GetInterpreterStateUnsafe(); if (interp == NULL) { /* We need the interpreter state to get Python threads */ return "unable to get the interpreter state"; } } else { interp = current_tstate->interp; } } assert(interp != NULL); /* Get the current interpreter from the current thread */ tstate = PyInterpreterState_ThreadHead(interp); if (tstate == NULL) return "unable to get the thread head state"; /* Dump the traceback of each thread */ tstate = PyInterpreterState_ThreadHead(interp); nthreads = 0; _Py_BEGIN_SUPPRESS_IPH do { if (nthreads != 0) PUTS(fd, "\n"); if (nthreads >= MAX_NTHREADS) { PUTS(fd, "...\n"); break; } write_thread_id(fd, tstate, tstate == current_tstate); if (tstate == current_tstate && tstate->interp->gc.collecting) { PUTS(fd, " Garbage-collecting\n"); } dump_traceback(fd, tstate, 0); tstate = PyThreadState_Next(tstate); nthreads++; } while (tstate != NULL); _Py_END_SUPPRESS_IPH return NULL; }