From 3e0055f8c65c407e74ce476b8e2b1fb889723514 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 20 Oct 2005 19:59:25 +0000 Subject: Merge ast-branch to head This change implements a new bytecode compiler, based on a transformation of the parse tree to an abstract syntax defined in Parser/Python.asdl. The compiler implementation is not complete, but it is in stable enough shape to run the entire test suite excepting two disabled tests. --- Include/Python-ast.h | 418 ++ Include/Python.h | 3 +- Include/asdl.h | 54 + Include/ast.h | 13 + Include/code.h | 73 + Include/compile.h | 71 +- Include/pyport.h | 1 + Include/pythonrun.h | 72 +- Include/symtable.h | 77 +- Lib/cgitb.py | 1 + Lib/compiler/pyassem.py | 19 +- Lib/pydoc.py | 1 + Lib/test/output/test_grammar | 1 + Lib/test/output/test_profile | 2 +- Lib/test/test_code.py | 85 + Lib/test/test_doctest.py | 12 +- Lib/test/test_eof.py | 8 +- Lib/test/test_generators.py | 2 +- Lib/test/test_genexps.py | 5 +- Lib/test/test_grammar.py | 25 +- Lib/test/test_import.py | 13 + Lib/test/test_parser.py | 24 +- Lib/test/test_repr.py | 2 +- Lib/test/test_scope.py | 105 +- Makefile.pre.in | 20 +- Misc/ACKS | 1 + Modules/_hotshot.c | 1 + Modules/symtablemodule.c | 8 +- Objects/codeobject.c | 453 +++ Objects/frameobject.c | 1 + Objects/funcobject.c | 6 +- Objects/typeobject.c | 12 +- PCbuild/pythoncore.vcproj | 130 +- Parser/.cvsignore | 3 + Parser/Python.asdl | 107 + Parser/asdl.py | 393 ++ Parser/asdl_c.py | 621 +++ Parser/grammar.mak | 2 +- Parser/parsetok.c | 5 +- Parser/spark.py | 840 ++++ Python/Python-ast.c | 2281 +++++++++++ Python/asdl.c | 92 + Python/ast.c | 3114 +++++++++++++++ Python/bltinmodule.c | 2 +- Python/ceval.c | 45 +- Python/compile.c | 8837 +++++++++++++++--------------------------- Python/future.c | 276 +- Python/import.c | 23 +- Python/marshal.c | 1 + Python/pythonrun.c | 276 +- Python/symtable.c | 1241 +++++- Python/sysmodule.c | 2 +- Python/traceback.c | 2 +- Tools/compiler/dumppyc.py | 11 +- 54 files changed, 13379 insertions(+), 6514 deletions(-) create mode 100644 Include/Python-ast.h create mode 100644 Include/asdl.h create mode 100644 Include/ast.h create mode 100644 Include/code.h create mode 100644 Lib/test/test_code.py create mode 100644 Objects/codeobject.c create mode 100644 Parser/Python.asdl create mode 100644 Parser/asdl.py create mode 100755 Parser/asdl_c.py create mode 100644 Parser/spark.py create mode 100644 Python/Python-ast.c create mode 100644 Python/asdl.c create mode 100644 Python/ast.c diff --git a/Include/Python-ast.h b/Include/Python-ast.h new file mode 100644 index 0000000..0859acf --- /dev/null +++ b/Include/Python-ast.h @@ -0,0 +1,418 @@ +/* File automatically generated by ../Parser/asdl_c.py */ + +#include "asdl.h" + +typedef struct _mod *mod_ty; + +typedef struct _stmt *stmt_ty; + +typedef struct _expr *expr_ty; + +typedef enum _expr_context { Load=1, Store=2, Del=3, AugLoad=4, AugStore=5, + Param=6 } expr_context_ty; + +typedef struct _slice *slice_ty; + +typedef enum _boolop { And=1, Or=2 } boolop_ty; + +typedef enum _operator { Add=1, Sub=2, Mult=3, Div=4, Mod=5, Pow=6, LShift=7, + RShift=8, BitOr=9, BitXor=10, BitAnd=11, FloorDiv=12 } + operator_ty; + +typedef enum _unaryop { Invert=1, Not=2, UAdd=3, USub=4 } unaryop_ty; + +typedef enum _cmpop { Eq=1, NotEq=2, Lt=3, LtE=4, Gt=5, GtE=6, Is=7, IsNot=8, + In=9, NotIn=10 } cmpop_ty; + +typedef struct _comprehension *comprehension_ty; + +typedef struct _excepthandler *excepthandler_ty; + +typedef struct _arguments *arguments_ty; + +typedef struct _keyword *keyword_ty; + +typedef struct _alias *alias_ty; + +struct _mod { + enum { Module_kind=1, Interactive_kind=2, Expression_kind=3, + Suite_kind=4 } kind; + union { + struct { + asdl_seq *body; + } Module; + + struct { + asdl_seq *body; + } Interactive; + + struct { + expr_ty body; + } Expression; + + struct { + asdl_seq *body; + } Suite; + + } v; +}; + +struct _stmt { + enum { FunctionDef_kind=1, ClassDef_kind=2, Return_kind=3, + Delete_kind=4, Assign_kind=5, AugAssign_kind=6, Print_kind=7, + For_kind=8, While_kind=9, If_kind=10, Raise_kind=11, + TryExcept_kind=12, TryFinally_kind=13, Assert_kind=14, + Import_kind=15, ImportFrom_kind=16, Exec_kind=17, + Global_kind=18, Expr_kind=19, Pass_kind=20, Break_kind=21, + Continue_kind=22 } kind; + union { + struct { + identifier name; + arguments_ty args; + asdl_seq *body; + asdl_seq *decorators; + } FunctionDef; + + struct { + identifier name; + asdl_seq *bases; + asdl_seq *body; + } ClassDef; + + struct { + expr_ty value; + } Return; + + struct { + asdl_seq *targets; + } Delete; + + struct { + asdl_seq *targets; + expr_ty value; + } Assign; + + struct { + expr_ty target; + operator_ty op; + expr_ty value; + } AugAssign; + + struct { + expr_ty dest; + asdl_seq *values; + bool nl; + } Print; + + struct { + expr_ty target; + expr_ty iter; + asdl_seq *body; + asdl_seq *orelse; + } For; + + struct { + expr_ty test; + asdl_seq *body; + asdl_seq *orelse; + } While; + + struct { + expr_ty test; + asdl_seq *body; + asdl_seq *orelse; + } If; + + struct { + expr_ty type; + expr_ty inst; + expr_ty tback; + } Raise; + + struct { + asdl_seq *body; + asdl_seq *handlers; + asdl_seq *orelse; + } TryExcept; + + struct { + asdl_seq *body; + asdl_seq *finalbody; + } TryFinally; + + struct { + expr_ty test; + expr_ty msg; + } Assert; + + struct { + asdl_seq *names; + } Import; + + struct { + identifier module; + asdl_seq *names; + } ImportFrom; + + struct { + expr_ty body; + expr_ty globals; + expr_ty locals; + } Exec; + + struct { + asdl_seq *names; + } Global; + + struct { + expr_ty value; + } Expr; + + } v; + int lineno; +}; + +struct _expr { + enum { BoolOp_kind=1, BinOp_kind=2, UnaryOp_kind=3, Lambda_kind=4, + Dict_kind=5, ListComp_kind=6, GeneratorExp_kind=7, Yield_kind=8, + Compare_kind=9, Call_kind=10, Repr_kind=11, Num_kind=12, + Str_kind=13, Attribute_kind=14, Subscript_kind=15, Name_kind=16, + List_kind=17, Tuple_kind=18 } kind; + union { + struct { + boolop_ty op; + asdl_seq *values; + } BoolOp; + + struct { + expr_ty left; + operator_ty op; + expr_ty right; + } BinOp; + + struct { + unaryop_ty op; + expr_ty operand; + } UnaryOp; + + struct { + arguments_ty args; + expr_ty body; + } Lambda; + + struct { + asdl_seq *keys; + asdl_seq *values; + } Dict; + + struct { + expr_ty elt; + asdl_seq *generators; + } ListComp; + + struct { + expr_ty elt; + asdl_seq *generators; + } GeneratorExp; + + struct { + expr_ty value; + } Yield; + + struct { + expr_ty left; + asdl_seq *ops; + asdl_seq *comparators; + } Compare; + + struct { + expr_ty func; + asdl_seq *args; + asdl_seq *keywords; + expr_ty starargs; + expr_ty kwargs; + } Call; + + struct { + expr_ty value; + } Repr; + + struct { + object n; + } Num; + + struct { + string s; + } Str; + + struct { + expr_ty value; + identifier attr; + expr_context_ty ctx; + } Attribute; + + struct { + expr_ty value; + slice_ty slice; + expr_context_ty ctx; + } Subscript; + + struct { + identifier id; + expr_context_ty ctx; + } Name; + + struct { + asdl_seq *elts; + expr_context_ty ctx; + } List; + + struct { + asdl_seq *elts; + expr_context_ty ctx; + } Tuple; + + } v; + int lineno; +}; + +struct _slice { + enum { Ellipsis_kind=1, Slice_kind=2, ExtSlice_kind=3, Index_kind=4 } + kind; + union { + struct { + expr_ty lower; + expr_ty upper; + expr_ty step; + } Slice; + + struct { + asdl_seq *dims; + } ExtSlice; + + struct { + expr_ty value; + } Index; + + } v; +}; + +struct _comprehension { + expr_ty target; + expr_ty iter; + asdl_seq *ifs; +}; + +struct _excepthandler { + expr_ty type; + expr_ty name; + asdl_seq *body; +}; + +struct _arguments { + asdl_seq *args; + identifier vararg; + identifier kwarg; + asdl_seq *defaults; +}; + +struct _keyword { + identifier arg; + expr_ty value; +}; + +struct _alias { + identifier name; + identifier asname; +}; + +mod_ty Module(asdl_seq * body); +mod_ty Interactive(asdl_seq * body); +mod_ty Expression(expr_ty body); +mod_ty Suite(asdl_seq * body); +stmt_ty FunctionDef(identifier name, arguments_ty args, asdl_seq * body, + asdl_seq * decorators, int lineno); +stmt_ty ClassDef(identifier name, asdl_seq * bases, asdl_seq * body, int + lineno); +stmt_ty Return(expr_ty value, int lineno); +stmt_ty Delete(asdl_seq * targets, int lineno); +stmt_ty Assign(asdl_seq * targets, expr_ty value, int lineno); +stmt_ty AugAssign(expr_ty target, operator_ty op, expr_ty value, int lineno); +stmt_ty Print(expr_ty dest, asdl_seq * values, bool nl, int lineno); +stmt_ty For(expr_ty target, expr_ty iter, asdl_seq * body, asdl_seq * orelse, + int lineno); +stmt_ty While(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno); +stmt_ty If(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno); +stmt_ty Raise(expr_ty type, expr_ty inst, expr_ty tback, int lineno); +stmt_ty TryExcept(asdl_seq * body, asdl_seq * handlers, asdl_seq * orelse, int + lineno); +stmt_ty TryFinally(asdl_seq * body, asdl_seq * finalbody, int lineno); +stmt_ty Assert(expr_ty test, expr_ty msg, int lineno); +stmt_ty Import(asdl_seq * names, int lineno); +stmt_ty ImportFrom(identifier module, asdl_seq * names, int lineno); +stmt_ty Exec(expr_ty body, expr_ty globals, expr_ty locals, int lineno); +stmt_ty Global(asdl_seq * names, int lineno); +stmt_ty Expr(expr_ty value, int lineno); +stmt_ty Pass(int lineno); +stmt_ty Break(int lineno); +stmt_ty Continue(int lineno); +expr_ty BoolOp(boolop_ty op, asdl_seq * values, int lineno); +expr_ty BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno); +expr_ty UnaryOp(unaryop_ty op, expr_ty operand, int lineno); +expr_ty Lambda(arguments_ty args, expr_ty body, int lineno); +expr_ty Dict(asdl_seq * keys, asdl_seq * values, int lineno); +expr_ty ListComp(expr_ty elt, asdl_seq * generators, int lineno); +expr_ty GeneratorExp(expr_ty elt, asdl_seq * generators, int lineno); +expr_ty Yield(expr_ty value, int lineno); +expr_ty Compare(expr_ty left, asdl_seq * ops, asdl_seq * comparators, int + lineno); +expr_ty Call(expr_ty func, asdl_seq * args, asdl_seq * keywords, expr_ty + starargs, expr_ty kwargs, int lineno); +expr_ty Repr(expr_ty value, int lineno); +expr_ty Num(object n, int lineno); +expr_ty Str(string s, int lineno); +expr_ty Attribute(expr_ty value, identifier attr, expr_context_ty ctx, int + lineno); +expr_ty Subscript(expr_ty value, slice_ty slice, expr_context_ty ctx, int + lineno); +expr_ty Name(identifier id, expr_context_ty ctx, int lineno); +expr_ty List(asdl_seq * elts, expr_context_ty ctx, int lineno); +expr_ty Tuple(asdl_seq * elts, expr_context_ty ctx, int lineno); +slice_ty Ellipsis(void); +slice_ty Slice(expr_ty lower, expr_ty upper, expr_ty step); +slice_ty ExtSlice(asdl_seq * dims); +slice_ty Index(expr_ty value); +comprehension_ty comprehension(expr_ty target, expr_ty iter, asdl_seq * ifs); +excepthandler_ty excepthandler(expr_ty type, expr_ty name, asdl_seq * body); +arguments_ty arguments(asdl_seq * args, identifier vararg, identifier kwarg, + asdl_seq * defaults); +keyword_ty keyword(identifier arg, expr_ty value); +alias_ty alias(identifier name, identifier asname); +void free_mod(mod_ty); +void free_stmt(stmt_ty); +void free_expr(expr_ty); +void free_expr_context(expr_context_ty); +void free_slice(slice_ty); +void free_boolop(boolop_ty); +void free_operator(operator_ty); +void free_unaryop(unaryop_ty); +void free_cmpop(cmpop_ty); +void free_comprehension(comprehension_ty); +void free_excepthandler(excepthandler_ty); +void free_arguments(arguments_ty); +void free_keyword(keyword_ty); +void free_alias(alias_ty); +int marshal_write_mod(PyObject **, int *, mod_ty); +int marshal_write_stmt(PyObject **, int *, stmt_ty); +int marshal_write_expr(PyObject **, int *, expr_ty); +int marshal_write_expr_context(PyObject **, int *, expr_context_ty); +int marshal_write_slice(PyObject **, int *, slice_ty); +int marshal_write_boolop(PyObject **, int *, boolop_ty); +int marshal_write_operator(PyObject **, int *, operator_ty); +int marshal_write_unaryop(PyObject **, int *, unaryop_ty); +int marshal_write_cmpop(PyObject **, int *, cmpop_ty); +int marshal_write_comprehension(PyObject **, int *, comprehension_ty); +int marshal_write_excepthandler(PyObject **, int *, excepthandler_ty); +int marshal_write_arguments(PyObject **, int *, arguments_ty); +int marshal_write_keyword(PyObject **, int *, keyword_ty); +int marshal_write_alias(PyObject **, int *, alias_ty); diff --git a/Include/Python.h b/Include/Python.h index 2d48d2e..f941cba 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -128,8 +128,7 @@ #include "pystrtod.h" /* _Py_Mangle is defined in compile.c */ -PyAPI_FUNC(int) _Py_Mangle(char *p, char *name, \ - char *buffer, size_t maxlen); +PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name); /* PyArg_GetInt is deprecated and should not be used, use PyArg_Parse(). */ #define PyArg_GetInt(v, a) PyArg_Parse((v), "i", (a)) diff --git a/Include/asdl.h b/Include/asdl.h new file mode 100644 index 0000000..8ad46fa --- /dev/null +++ b/Include/asdl.h @@ -0,0 +1,54 @@ +#ifndef Py_ASDL_H +#define Py_ASDL_H + +typedef PyObject * identifier; +typedef PyObject * string; +typedef PyObject * object; + +typedef enum {false, true} bool; + +/* It would be nice if the code generated by asdl_c.py was completely + independent of Python, but it is a goal the requires too much work + at this stage. So, for example, I'll represent identifiers as + interned Python strings. +*/ + +/* XXX A sequence should be typed so that its use can be typechecked. */ + +/* XXX We shouldn't pay for offset when we don't need APPEND. */ + +typedef struct { + int size; + int offset; + void *elements[1]; +} asdl_seq; + +asdl_seq *asdl_seq_new(int size); +void asdl_seq_free(asdl_seq *); + +#ifdef Py_DEBUG +#define asdl_seq_GET(S, I) (S)->elements[(I)] +#define asdl_seq_SET(S, I, V) { \ + int _asdl_i = (I); \ + assert((S) && _asdl_i < (S)->size); \ + (S)->elements[_asdl_i] = (V); \ +} +#define asdl_seq_APPEND(S, V) { \ + assert((S) && (S)->offset < (S)->size); \ + (S)->elements[(S)->offset++] = (V); \ +} +#else +#define asdl_seq_GET(S, I) (S)->elements[(I)] +#define asdl_seq_SET(S, I, V) (S)->elements[I] = (V) +#define asdl_seq_APPEND(S, V) (S)->elements[(S)->offset++] = (V) +#endif +#define asdl_seq_LEN(S) ((S) == NULL ? 0 : (S)->size) + +/* Routines to marshal the basic types. */ +int marshal_write_int(PyObject **, int *, int); +int marshal_write_bool(PyObject **, int *, bool); +int marshal_write_identifier(PyObject **, int *, identifier); +int marshal_write_string(PyObject **, int *, string); +int marshal_write_object(PyObject **, int *, object); + +#endif /* !Py_ASDL_H */ diff --git a/Include/ast.h b/Include/ast.h new file mode 100644 index 0000000..8912f38 --- /dev/null +++ b/Include/ast.h @@ -0,0 +1,13 @@ +#ifndef Py_AST_H +#define Py_AST_H +#ifdef __cplusplus +extern "C" { +#endif + +extern DL_IMPORT(mod_ty) PyAST_FromNode(const node *, PyCompilerFlags *flags, + const char *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_AST_H */ diff --git a/Include/code.h b/Include/code.h new file mode 100644 index 0000000..e6478b0 --- /dev/null +++ b/Include/code.h @@ -0,0 +1,73 @@ +/* Definitions for bytecode */ + +#ifndef Py_CODE_H +#define Py_CODE_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Bytecode object */ +typedef struct { + PyObject_HEAD + int co_argcount; /* #arguments, except *args */ + int co_nlocals; /* #local variables */ + int co_stacksize; /* #entries needed for evaluation stack */ + int co_flags; /* CO_..., see below */ + PyObject *co_code; /* instruction opcodes */ + PyObject *co_consts; /* list (constants used) */ + PyObject *co_names; /* list of strings (names used) */ + PyObject *co_varnames; /* tuple of strings (local variable names) */ + PyObject *co_freevars; /* tuple of strings (free variable names) */ + PyObject *co_cellvars; /* tuple of strings (cell variable names) */ + /* The rest doesn't count for hash/cmp */ + PyObject *co_filename; /* string (where it was loaded from) */ + PyObject *co_name; /* string (name, for reference) */ + int co_firstlineno; /* first source line number */ + PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) */ +} PyCodeObject; + +/* Masks for co_flags above */ +#define CO_OPTIMIZED 0x0001 +#define CO_NEWLOCALS 0x0002 +#define CO_VARARGS 0x0004 +#define CO_VARKEYWORDS 0x0008 +#define CO_NESTED 0x0010 +#define CO_GENERATOR 0x0020 +/* The CO_NOFREE flag is set if there are no free or cell variables. + This information is redundant, but it allows a single flag test + to determine whether there is any extra work to be done when the + call frame it setup. +*/ +#define CO_NOFREE 0x0040 +/* XXX Temporary hack. Until generators are a permanent part of the + language, we need a way for a code object to record that generators + were *possible* when it was compiled. This is so code dynamically + compiled *by* a code object knows whether to allow yield stmts. In + effect, this passes on the "from __future__ import generators" state + in effect when the code block was compiled. */ +#define CO_GENERATOR_ALLOWED 0x1000 /* no longer used in an essential way */ +#define CO_FUTURE_DIVISION 0x2000 + +#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */ + +extern DL_IMPORT(PyTypeObject) PyCode_Type; + +#define PyCode_Check(op) ((op)->ob_type == &PyCode_Type) +#define PyCode_GetNumFree(op) (PyTuple_GET_SIZE((op)->co_freevars)) + +/* Public interface */ +DL_IMPORT(PyCodeObject *) PyCode_New( + int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, + PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *); + /* same as struct above */ +DL_IMPORT(int) PyCode_Addr2Line(PyCodeObject *, int); + +/* for internal use only */ +#define _PyCode_GETCODEPTR(co, pp) \ + ((*(co)->co_code->ob_type->tp_as_buffer->bf_getreadbuffer) \ + ((co)->co_code, 0, (void **)(pp))) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CODE_H */ diff --git a/Include/compile.h b/Include/compile.h index 82bf708..27a3f76 100644 --- a/Include/compile.h +++ b/Include/compile.h @@ -1,5 +1,6 @@ - -/* Definitions for bytecode */ +#ifndef Py_CODE_H +#include "code.h" +#endif #ifndef Py_COMPILE_H #define Py_COMPILE_H @@ -7,55 +8,6 @@ extern "C" { #endif -/* Bytecode object */ -typedef struct { - PyObject_HEAD - int co_argcount; /* #arguments, except *args */ - int co_nlocals; /* #local variables */ - int co_stacksize; /* #entries needed for evaluation stack */ - int co_flags; /* CO_..., see below */ - PyObject *co_code; /* instruction opcodes */ - PyObject *co_consts; /* list (constants used) */ - PyObject *co_names; /* list of strings (names used) */ - PyObject *co_varnames; /* tuple of strings (local variable names) */ - PyObject *co_freevars; /* tuple of strings (free variable names) */ - PyObject *co_cellvars; /* tuple of strings (cell variable names) */ - /* The rest doesn't count for hash/cmp */ - PyObject *co_filename; /* string (where it was loaded from) */ - PyObject *co_name; /* string (name, for reference) */ - int co_firstlineno; /* first source line number */ - PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) */ -} PyCodeObject; - -/* Masks for co_flags above */ -#define CO_OPTIMIZED 0x0001 -#define CO_NEWLOCALS 0x0002 -#define CO_VARARGS 0x0004 -#define CO_VARKEYWORDS 0x0008 -#define CO_NESTED 0x0010 -#define CO_GENERATOR 0x0020 -/* The CO_NOFREE flag is set if there are no free or cell variables. - This information is redundant, but it allows a single flag test - to determine whether there is any extra work to be done when the - call frame it setup. -*/ -#define CO_NOFREE 0x0040 -/* XXX Temporary hack. Until generators are a permanent part of the - language, we need a way for a code object to record that generators - were *possible* when it was compiled. This is so code dynamically - compiled *by* a code object knows whether to allow yield stmts. In - effect, this passes on the "from __future__ import generators" state - in effect when the code block was compiled. */ -#define CO_GENERATOR_ALLOWED 0x1000 /* no longer used in an essential way */ -#define CO_FUTURE_DIVISION 0x2000 - -PyAPI_DATA(PyTypeObject) PyCode_Type; - -#define PyCode_Check(op) ((op)->ob_type == &PyCode_Type) -#define PyCode_GetNumFree(op) (PyTuple_GET_SIZE((op)->co_freevars)) - -#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */ - /* Public interface */ struct _node; /* Declare the existence of this type */ PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *); @@ -68,19 +20,22 @@ PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int); /* Future feature support */ typedef struct { - int ff_found_docstring; - int ff_last_lineno; - int ff_features; + int ff_features; /* flags set by future statements */ + int ff_lineno; /* line number of last future statement */ } PyFutureFeatures; -PyAPI_FUNC(PyFutureFeatures *) PyNode_Future(struct _node *, const char *); -PyAPI_FUNC(PyCodeObject *) PyNode_CompileFlags(struct _node *, const char *, - PyCompilerFlags *); - #define FUTURE_NESTED_SCOPES "nested_scopes" #define FUTURE_GENERATORS "generators" #define FUTURE_DIVISION "division" +struct _mod; /* Declare the existence of this type */ +DL_IMPORT(PyCodeObject *) PyAST_Compile(struct _mod *, const char *, + PyCompilerFlags *); +DL_IMPORT(PyFutureFeatures *) PyFuture_FromAST(struct _mod *, const char *); + +#define ERR_LATE_FUTURE \ +"from __future__ imports must occur at the beginning of the file" + #ifdef __cplusplus } #endif diff --git a/Include/pyport.h b/Include/pyport.h index 2440c55..ea2091b 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -583,6 +583,7 @@ typedef struct fd_set { #ifndef INT_MAX #define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) #endif #ifndef LONG_MAX diff --git a/Include/pythonrun.h b/Include/pythonrun.h index f461d13..490613e 100644 --- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -29,46 +29,37 @@ PyAPI_FUNC(int) Py_IsInitialized(void); PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void); PyAPI_FUNC(void) Py_EndInterpreter(PyThreadState *); -PyAPI_FUNC(int) PyRun_AnyFile(FILE *, const char *); -PyAPI_FUNC(int) PyRun_AnyFileEx(FILE *, const char *, int); - -PyAPI_FUNC(int) PyRun_AnyFileFlags(FILE *, const char *, PyCompilerFlags *); -PyAPI_FUNC(int) PyRun_AnyFileExFlags(FILE *, const char *, int, PyCompilerFlags *); - -PyAPI_FUNC(int) PyRun_SimpleString(const char *); +PyAPI_FUNC(int) PyRun_AnyFileFlags(FILE *, char *, PyCompilerFlags *); +PyAPI_FUNC(int) PyRun_AnyFileExFlags(FILE *, char *, int, PyCompilerFlags *); PyAPI_FUNC(int) PyRun_SimpleStringFlags(const char *, PyCompilerFlags *); -PyAPI_FUNC(int) PyRun_SimpleFile(FILE *, const char *); -PyAPI_FUNC(int) PyRun_SimpleFileEx(FILE *, const char *, int); PyAPI_FUNC(int) PyRun_SimpleFileExFlags(FILE *, const char *, int, PyCompilerFlags *); -PyAPI_FUNC(int) PyRun_InteractiveOne(FILE *, const char *); PyAPI_FUNC(int) PyRun_InteractiveOneFlags(FILE *, const char *, PyCompilerFlags *); -PyAPI_FUNC(int) PyRun_InteractiveLoop(FILE *, const char *); PyAPI_FUNC(int) PyRun_InteractiveLoopFlags(FILE *, const char *, PyCompilerFlags *); -PyAPI_FUNC(struct _node *) PyParser_SimpleParseString(const char *, int); -PyAPI_FUNC(struct _node *) PyParser_SimpleParseFile(FILE *, const char *, int); -PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlags(const char *, int, int); -PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlagsFilename(const char *, - const char *, - int, - int); +PyAPI_FUNC(struct _mod *) PyParser_ASTFromString(const char *, const char *, + int, PyCompilerFlags *flags); +PyAPI_FUNC(struct _mod *) PyParser_ASTFromFile(FILE *, const char *, int, + char *, char *, + PyCompilerFlags *, int *); +#define PyParser_SimpleParseString(S, B) \ + PyParser_SimpleParseStringFlags(S, B, 0) +#define PyParser_SimpleParseFile(FP, S, B) \ + PyParser_SimpleParseFileFlags(FP, S, B, 0) +PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlags(const char *, int, + int); PyAPI_FUNC(struct _node *) PyParser_SimpleParseFileFlags(FILE *, const char *, int, int); -PyAPI_FUNC(PyObject *) PyRun_String(const char *, int, PyObject *, PyObject *); -PyAPI_FUNC(PyObject *) PyRun_File(FILE *, const char *, int, PyObject *, PyObject *); -PyAPI_FUNC(PyObject *) PyRun_FileEx(FILE *, const char *, int, - PyObject *, PyObject *, int); -PyAPI_FUNC(PyObject *) PyRun_StringFlags(const char *, int, PyObject *, PyObject *, - PyCompilerFlags *); -PyAPI_FUNC(PyObject *) PyRun_FileFlags(FILE *, const char *, int, PyObject *, - PyObject *, PyCompilerFlags *); -PyAPI_FUNC(PyObject *) PyRun_FileExFlags(FILE *, const char *, int, PyObject *, - PyObject *, int, PyCompilerFlags *); - -PyAPI_FUNC(PyObject *) Py_CompileString(const char *, const char *, int); +PyAPI_FUNC(PyObject *) PyRun_StringFlags(const char *, int, PyObject *, + PyObject *, PyCompilerFlags *); + +PyAPI_FUNC(PyObject *) PyRun_FileExFlags(FILE *, const char *, int, + PyObject *, PyObject *, int, + PyCompilerFlags *); + +#define Py_CompileString(str, p, s) Py_CompileStringFlags(str, p, s, NULL) PyAPI_FUNC(PyObject *) Py_CompileStringFlags(const char *, const char *, int, - PyCompilerFlags *); + PyCompilerFlags *); PyAPI_FUNC(struct symtable *) Py_SymtableString(const char *, const char *, int); PyAPI_FUNC(void) PyErr_Print(void); @@ -84,6 +75,25 @@ PyAPI_FUNC(int) Py_FdIsInteractive(FILE *, const char *); /* Bootstrap */ PyAPI_FUNC(int) Py_Main(int argc, char **argv); +/* Use macros for a bunch of old variants */ +#define PyRun_String(str, s, g, l) PyRun_StringFlags(str, s, g, l, NULL) +#define PyRun_AnyFile(fp, name) PyRun_AnyFileExFlags(fp, name, 0, NULL) +#define PyRun_AnyFileEx(fp, name, closeit) \ + PyRun_AnyFileExFlags(fp, name, closeit, NULL) +#define PyRun_AnyFileFlags(fp, name, flags) \ + PyRun_AnyFileExFlags(fp, name, 0, flags) +#define PyRun_SimpleString(s, f) PyRunSimpleStringFlags(s, f, NULL) +#define PyRun_SimpleFile(f, p) PyRun_SimpleFileExFlags(f, p, 0, NULL) +#define PyRun_SimpleFileEx(f, p, c) PyRun_SimpleFileExFlags(f, p, c, NULL) +#define PyRun_InteractiveOne(f, p) PyRun_InteractiveOneFlags(f, p, NULL) +#define PyRun_InteractiveLoop(f, p) PyRun_InteractiveLoopFlags(f, p, NULL) +#define PyRun_File(fp, p, s, g, l) \ + PyRun_FileExFlags(fp, p, s, g, l, 0, NULL) +#define PyRun_FileEx(fp, p, s, g, l, c) \ + PyRun_FileExFlags(fp, p, s, g, l, c, NULL) +#define PyRun_FileFlags(fp, p, s, g, l, flags) \ + PyRun_FileExFlags(fp, p, s, g, l, 0, flags) + /* In getpath.c */ PyAPI_FUNC(char *) Py_GetProgramFullPath(void); PyAPI_FUNC(char *) Py_GetPrefix(void); diff --git a/Include/symtable.h b/Include/symtable.h index 628c3e6..646602c 100644 --- a/Include/symtable.h +++ b/Include/symtable.h @@ -4,64 +4,59 @@ extern "C" { #endif -/* A symbol table is constructed each time PyNode_Compile() is - called. The table walks the entire parse tree and identifies each - use or definition of a variable. - - The symbol table contains a dictionary for each code block in a - module: The symbol dictionary for the block. They keys of these - dictionaries are the name of all variables used or defined in the - block; the integer values are used to store several flags, - e.g. DEF_PARAM indicates that a variable is a parameter to a - function. -*/ +typedef enum _block_type { FunctionBlock, ClassBlock, ModuleBlock } + block_ty; struct _symtable_entry; struct symtable { - int st_pass; /* pass == 1 or 2 */ const char *st_filename; /* name of file being compiled */ struct _symtable_entry *st_cur; /* current symbol table entry */ + struct _symtable_entry *st_top; /* module entry */ PyObject *st_symbols; /* dictionary of symbol table entries */ PyObject *st_stack; /* stack of namespace info */ PyObject *st_global; /* borrowed ref to MODULE in st_symbols */ - int st_nscopes; /* number of scopes */ - int st_errors; /* number of errors */ + int st_nblocks; /* number of blocks */ char *st_private; /* name of current class or NULL */ + int st_tmpname; /* temporary name counter */ PyFutureFeatures *st_future; /* module's future features */ }; typedef struct _symtable_entry { PyObject_HEAD - PyObject *ste_id; /* int: key in st_symbols) */ - PyObject *ste_symbols; /* dict: name to flags) */ - PyObject *ste_name; /* string: name of scope */ + PyObject *ste_id; /* int: key in st_symbols */ + PyObject *ste_symbols; /* dict: name to flags */ + PyObject *ste_name; /* string: name of block */ PyObject *ste_varnames; /* list of variable names */ PyObject *ste_children; /* list of child ids */ - int ste_type; /* module, class, or function */ - int ste_lineno; /* first line of scope */ - int ste_optimized; /* true if namespace can't be optimized */ - int ste_nested; /* true if scope is nested */ - int ste_child_free; /* true if a child scope has free variables, + block_ty ste_type; /* module, class, or function */ + int ste_unoptimized; /* false if namespace is optimized */ + int ste_nested : 1; /* true if block is nested */ + int ste_free : 1; /* true if block has free variables */ + int ste_child_free : 1; /* true if a child block has free variables, including free refs to globals */ - int ste_generator; /* true if namespace is a generator */ + int ste_generator : 1; /* true if namespace is a generator */ + int ste_varargs : 1; /* true if block has varargs */ + int ste_varkeywords : 1; /* true if block has varkeywords */ + int ste_lineno; /* first line of block */ int ste_opt_lineno; /* lineno of last exec or import * */ - int ste_tmpname; /* temporary name counter */ + int ste_tmpname; /* counter for listcomp temp vars */ struct symtable *ste_table; -} PySymtableEntryObject; - -PyAPI_DATA(PyTypeObject) PySymtableEntry_Type; +} PySTEntryObject; -#define PySymtableEntry_Check(op) ((op)->ob_type == &PySymtableEntry_Type) +PyAPI_DATA(PyTypeObject) PySTEntry_Type; -PyAPI_FUNC(PyObject *) PySymtableEntry_New(struct symtable *, - char *, int, int); +#define PySTEntry_Check(op) ((op)->ob_type == &PySTEntry_Type) -PyAPI_FUNC(struct symtable *) PyNode_CompileSymtable(struct _node *, const char *); -PyAPI_FUNC(void) PySymtable_Free(struct symtable *); +PyAPI_FUNC(PySTEntryObject *) \ + PySTEntry_New(struct symtable *, identifier, block_ty, void *, int); +PyAPI_FUNC(int) PyST_GetScope(PySTEntryObject *, PyObject *); +PyAPI_FUNC(struct symtable *) PySymtable_Build(mod_ty, const char *, + PyFutureFeatures *); +PyAPI_FUNC(PySTEntryObject *) PySymtable_Lookup(struct symtable *, void *); -#define TOP "global" +PyAPI_FUNC(void) PySymtable_Free(struct symtable *); /* Flags for def-use information */ @@ -72,16 +67,19 @@ PyAPI_FUNC(void) PySymtable_Free(struct symtable *); #define DEF_STAR 2<<3 /* parameter is star arg */ #define DEF_DOUBLESTAR 2<<4 /* parameter is star-star arg */ #define DEF_INTUPLE 2<<5 /* name defined in tuple in parameters */ -#define DEF_FREE 2<<6 /* name used but not defined in nested scope */ +#define DEF_FREE 2<<6 /* name used but not defined in nested block */ #define DEF_FREE_GLOBAL 2<<7 /* free variable is actually implicit global */ #define DEF_FREE_CLASS 2<<8 /* free variable from class's method */ #define DEF_IMPORT 2<<9 /* assignment occurred via import */ #define DEF_BOUND (DEF_LOCAL | DEF_PARAM | DEF_IMPORT) -#define TYPE_FUNCTION 1 -#define TYPE_CLASS 2 -#define TYPE_MODULE 3 +/* GLOBAL_EXPLICIT and GLOBAL_IMPLICIT are used internally by the symbol + table. GLOBAL is returned from PyST_GetScope() for either of them. + It is stored in ste_symbols at bits 12-14. +*/ +#define SCOPE_OFF 11 +#define SCOPE_MASK 7 #define LOCAL 1 #define GLOBAL_EXPLICIT 2 @@ -89,9 +87,14 @@ PyAPI_FUNC(void) PySymtable_Free(struct symtable *); #define FREE 4 #define CELL 5 +/* The following three names are used for the ste_unoptimized bit field */ #define OPT_IMPORT_STAR 1 #define OPT_EXEC 2 #define OPT_BARE_EXEC 4 +#define OPT_TOPLEVEL 8 /* top-level names, including eval and exec */ + +#define GENERATOR 1 +#define GENERATOR_EXPRESSION 2 #define GENERATOR 1 #define GENERATOR_EXPRESSION 2 diff --git a/Lib/cgitb.py b/Lib/cgitb.py index 8d979b8..ae25cf1 100644 --- a/Lib/cgitb.py +++ b/Lib/cgitb.py @@ -22,6 +22,7 @@ The default handler displays output as HTML. """ __author__ = 'Ka-Ping Yee' + __version__ = '$Revision$' import sys diff --git a/Lib/compiler/pyassem.py b/Lib/compiler/pyassem.py index 0547eeb..e1fb063 100644 --- a/Lib/compiler/pyassem.py +++ b/Lib/compiler/pyassem.py @@ -364,16 +364,15 @@ class PyFlowGraph(FlowGraph): def getCode(self): """Get a Python code object""" - if self.stage == RAW: - self.computeStackDepth() - self.flattenGraph() - if self.stage == FLAT: - self.convertArgs() - if self.stage == CONV: - self.makeByteCode() - if self.stage == DONE: - return self.newCodeObject() - raise RuntimeError, "inconsistent PyFlowGraph state" + assert self.stage == RAW + self.computeStackDepth() + self.flattenGraph() + assert self.stage == FLAT + self.convertArgs() + assert self.stage == CONV + self.makeByteCode() + assert self.stage == DONE + return self.newCodeObject() def dump(self, io=None): if io: diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 4084b7e..5d16fa5 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -36,6 +36,7 @@ Reference Manual pages. __author__ = "Ka-Ping Yee " __date__ = "26 February 2001" + __version__ = "$Revision$" __credits__ = """Guido van Rossum, for an excellent programming language. Tommy Burnette, the original creator of manpy. diff --git a/Lib/test/output/test_grammar b/Lib/test/output/test_grammar index 6174e7a..fed4197 100644 --- a/Lib/test/output/test_grammar +++ b/Lib/test/output/test_grammar @@ -34,6 +34,7 @@ continue + try/except ok continue + try/finally ok testing continue and break in try/except in loop return_stmt +yield_stmt raise_stmt import_name import_from diff --git a/Lib/test/output/test_profile b/Lib/test/output/test_profile index b46bb6a..e745075 100644 --- a/Lib/test/output/test_profile +++ b/Lib/test/output/test_profile @@ -7,7 +7,7 @@ test_profile 12 0.000 0.000 0.012 0.001 :0(hasattr) 8 0.000 0.000 0.000 0.000 :0(range) 1 0.000 0.000 0.000 0.000 :0(setprofile) - 1 0.000 0.000 1.000 1.000 :1(?) + 1 0.000 0.000 1.000 1.000 :1() 0 0.000 0.000 profile:0(profiler) 1 0.000 0.000 1.000 1.000 profile:0(testfunc()) 1 0.400 0.400 1.000 1.000 test_profile.py:23(testfunc) diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py new file mode 100644 index 0000000..ff95c6a --- /dev/null +++ b/Lib/test/test_code.py @@ -0,0 +1,85 @@ +"""This module includes tests of the code object representation. + +>>> def f(x): +... def g(y): +... return x + y +... return g +... + +>>> dump(f.func_code) +name: f +argcount: 1 +names: () +varnames: ('x', 'g') +cellvars: ('x',) +freevars: () +nlocals: 2 +flags: 3 +consts: ('None', '') + +>>> dump(f(4).func_code) +name: g +argcount: 1 +names: () +varnames: ('y',) +cellvars: () +freevars: ('x',) +nlocals: 1 +flags: 19 +consts: ('None',) + +>>> def h(x, y): +... a = x + y +... b = x - y +... c = a * b +... return c +... +>>> dump(h.func_code) +name: h +argcount: 2 +names: () +varnames: ('x', 'y', 'a', 'b', 'c') +cellvars: () +freevars: () +nlocals: 5 +flags: 67 +consts: ('None',) + +>>> def attrs(obj): +... print obj.attr1 +... print obj.attr2 +... print obj.attr3 + +>>> dump(attrs.func_code) +name: attrs +argcount: 1 +names: ('attr1', 'attr2', 'attr3') +varnames: ('obj',) +cellvars: () +freevars: () +nlocals: 1 +flags: 67 +consts: ('None',) + +""" + +def consts(t): + """Yield a doctest-safe sequence of object reprs.""" + for elt in t: + r = repr(elt) + if r.startswith("" % elt.co_name + else: + yield r + +def dump(co): + """Print out a text representation of a code object.""" + for attr in ["name", "argcount", "names", "varnames", "cellvars", + "freevars", "nlocals", "flags"]: + print "%s: %s" % (attr, getattr(co, "co_" + attr)) + print "consts:", tuple(consts(co.co_consts)) + +def test_main(verbose=None): + from test.test_support import run_doctest + from test import test_code + run_doctest(test_code, verbose) diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index fc4841a..9c39ee8 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -1559,11 +1559,11 @@ Run the debugger on the docstring, and then restore sys.stdin. >>> try: doctest.debug_src(s) ... finally: sys.stdin = real_stdin - > (1)?() + > (1)() (Pdb) next 12 --Return-- - > (1)?()->None + > (1)()->None (Pdb) print x 12 (Pdb) continue @@ -1601,7 +1601,7 @@ def test_pdb_set_trace(): >>> try: runner.run(test) ... finally: sys.stdin = real_stdin --Return-- - > (1)?()->None + > (1)()->None -> import pdb; pdb.set_trace() (Pdb) print x 42 @@ -1637,7 +1637,7 @@ def test_pdb_set_trace(): (Pdb) print y 2 (Pdb) up - > (1)?() + > (1)() -> calls_set_trace() (Pdb) print x 1 @@ -1686,7 +1686,7 @@ def test_pdb_set_trace(): [EOF] (Pdb) next --Return-- - > (1)?()->None + > (1)()->None -> f(3) (Pdb) list 1 -> f(3) @@ -1779,7 +1779,7 @@ def test_pdb_set_trace_nested(): (Pdb) print y 1 (Pdb) up - > (1)?() + > (1)() -> calls_set_trace() (Pdb) print foo *** NameError: name 'foo' is not defined diff --git a/Lib/test/test_eof.py b/Lib/test/test_eof.py index 683649d..aae3518 100644 --- a/Lib/test/test_eof.py +++ b/Lib/test/test_eof.py @@ -7,21 +7,21 @@ from test import test_support class EOFTestCase(unittest.TestCase): def test_EOFC(self): + expect = "EOL while scanning single-quoted string (, line 1)" try: eval("""'this is a test\ """) except SyntaxError, msg: - self.assertEqual(str(msg), - "EOL while scanning single-quoted string (line 1)") + self.assertEqual(str(msg), expect) else: raise test_support.TestFailed def test_EOFS(self): + expect = "EOF while scanning triple-quoted string (, line 1)" try: eval("""'''this is a test""") except SyntaxError, msg: - self.assertEqual(str(msg), - "EOF while scanning triple-quoted string (line 1)") + self.assertEqual(str(msg), expect) else: raise test_support.TestFailed diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index d226043..cb7e992 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -774,7 +774,7 @@ These are fine: ... try: ... 1//0 ... except ZeroDivisionError: -... yield 666 # bad because *outer* try has finally +... yield 666 ... except: ... pass ... finally: diff --git a/Lib/test/test_genexps.py b/Lib/test/test_genexps.py index 7c6fe4a..894ce6a 100644 --- a/Lib/test/test_genexps.py +++ b/Lib/test/test_genexps.py @@ -125,13 +125,12 @@ Verify that syntax error's are raised for genexps used as lvalues >>> (y for y in (1,2)) = 10 Traceback (most recent call last): ... - SyntaxError: assign to generator expression not possible + SyntaxError: assignment to generator expression not possible (, line 1) >>> (y for y in (1,2)) += 10 Traceback (most recent call last): ... - SyntaxError: augmented assign to tuple literal, yield, or generator expression not possible - + SyntaxError: augmented assignment to generator expression not possible (, line 1) ########### Tests borrowed from or inspired by test_generators.py ############ diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 1b4a506..820fab5 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -8,7 +8,7 @@ # regression test, the filterwarnings() call has been added to # regrtest.py. -from test.test_support import TestFailed, verify, check_syntax +from test.test_support import TestFailed, verify, vereq, check_syntax import sys print '1. Parser' @@ -157,28 +157,31 @@ def f2(one_argument): pass def f3(two, arguments): pass def f4(two, (compound, (argument, list))): pass def f5((compound, first), two): pass -verify(f2.func_code.co_varnames == ('one_argument',)) -verify(f3.func_code.co_varnames == ('two', 'arguments')) +vereq(f2.func_code.co_varnames, ('one_argument',)) +vereq(f3.func_code.co_varnames, ('two', 'arguments')) if sys.platform.startswith('java'): - verify(f4.func_code.co_varnames == + vereq(f4.func_code.co_varnames, ('two', '(compound, (argument, list))', 'compound', 'argument', 'list',)) - verify(f5.func_code.co_varnames == + vereq(f5.func_code.co_varnames, ('(compound, first)', 'two', 'compound', 'first')) else: - verify(f4.func_code.co_varnames == ('two', '.2', 'compound', - 'argument', 'list')) - verify(f5.func_code.co_varnames == ('.0', 'two', 'compound', 'first')) + vereq(f4.func_code.co_varnames, + ('two', '.1', 'compound', 'argument', 'list')) + vereq(f5.func_code.co_varnames, + ('.0', 'two', 'compound', 'first')) def a1(one_arg,): pass def a2(two, args,): pass def v0(*rest): pass def v1(a, *rest): pass def v2(a, b, *rest): pass def v3(a, (b, c), *rest): return a, b, c, rest +# ceval unpacks the formal arguments into the first argcount names; +# thus, the names nested inside tuples must appear after these names. if sys.platform.startswith('java'): verify(v3.func_code.co_varnames == ('a', '(b, c)', 'rest', 'b', 'c')) else: - verify(v3.func_code.co_varnames == ('a', '.2', 'rest', 'b', 'c')) + vereq(v3.func_code.co_varnames, ('a', '.1', 'rest', 'b', 'c')) verify(v3(1, (2, 3), 4) == (1, 2, 3, (4,))) def d01(a=1): pass d01() @@ -410,6 +413,10 @@ def g1(): return def g2(): return 1 g1() x = g2() +check_syntax("class foo:return 1") + +print 'yield_stmt' +check_syntax("class foo:yield 1") print 'raise_stmt' # 'raise' test [',' test] try: raise RuntimeError, 'just testing' diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py index b89f09b..72f27fa 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -192,3 +192,16 @@ def test_failing_reload(): del sys.modules[TESTFN] test_failing_reload() + +def test_import_name_binding(): + # import x.y.z binds x in the current namespace + import test as x + import test.test_support + assert x is test, x.__name__ + assert hasattr(test.test_support, "__file__") + + # import x.y.z as w binds z as w + import test.test_support as y + assert y is test.test_support, y.__name__ + +test_import_name_binding() diff --git a/Lib/test/test_parser.py b/Lib/test/test_parser.py index d1ace67..771fe9d 100644 --- a/Lib/test/test_parser.py +++ b/Lib/test/test_parser.py @@ -411,10 +411,32 @@ class IllegalSyntaxTestCase(unittest.TestCase): (0, '')) self.check_bad_tree(tree, "malformed global ast") + +class CompileTestCase(unittest.TestCase): + + # These tests are very minimal. :-( + + def test_compile_expr(self): + st = parser.expr('2 + 3') + code = parser.compilest(st) + self.assertEquals(eval(code), 5) + + def test_compile_suite(self): + st = parser.suite('x = 2; y = x + 3') + code = parser.compilest(st) + globs = {} + exec code in globs + self.assertEquals(globs['y'], 5) + + def test_compile_error(self): + st = parser.suite('1 = 3 + 4') + self.assertRaises(SyntaxError, parser.compilest, st) + def test_main(): test_support.run_unittest( RoundtripLegalSyntaxTestCase, - IllegalSyntaxTestCase + IllegalSyntaxTestCase, + CompileTestCase, ) diff --git a/Lib/test/test_repr.py b/Lib/test/test_repr.py index 4ded484..a139473 100644 --- a/Lib/test/test_repr.py +++ b/Lib/test/test_repr.py @@ -123,7 +123,7 @@ class ReprTests(unittest.TestCase): def test_lambda(self): self.failUnless(repr(lambda x: x).startswith( - "") @@ -13,8 +13,8 @@ def make_adder(x): inc = make_adder(1) plus10 = make_adder(10) -verify(inc(1) == 2) -verify(plus10(-2) == 8) +vereq(inc(1), 2) +vereq(plus10(-2), 8) print "2. extra nesting" @@ -28,8 +28,8 @@ def make_adder2(x): inc = make_adder2(1) plus10 = make_adder2(10) -verify(inc(1) == 2) -verify(plus10(-2) == 8) +vereq(inc(1), 2) +vereq(plus10(-2), 8) print "3. simple nesting + rebinding" @@ -42,8 +42,8 @@ def make_adder3(x): inc = make_adder3(0) plus10 = make_adder3(9) -verify(inc(1) == 2) -verify(plus10(-2) == 8) +vereq(inc(1), 2) +vereq(plus10(-2), 8) print "4. nesting with global but no free" @@ -58,10 +58,10 @@ def make_adder4(): # XXX add exta level of indirection global_x = 1 adder = make_adder4() -verify(adder(1) == 2) +vereq(adder(1), 2) global_x = 10 -verify(adder(-2) == 8) +vereq(adder(-2), 8) print "5. nesting through class" @@ -74,8 +74,8 @@ def make_adder5(x): inc = make_adder5(1) plus10 = make_adder5(10) -verify(inc(1) == 2) -verify(plus10(-2) == 8) +vereq(inc(1), 2) +vereq(plus10(-2), 8) print "6. nesting plus free ref to global" @@ -89,8 +89,8 @@ def make_adder6(x): inc = make_adder6(1) plus10 = make_adder6(10) -verify(inc(1) == 11) # there's only one global -verify(plus10(-2) == 8) +vereq(inc(1), 11) # there's only one global +vereq(plus10(-2), 8) print "7. nearest enclosing scope" @@ -103,7 +103,7 @@ def f(x): return g(2) test_func = f(10) -verify(test_func(5) == 47) +vereq(test_func(5), 47) print "8. mixed freevars and cellvars" @@ -123,7 +123,7 @@ def f(x, y, z): g = f(1, 2, 3) h = g(2, 4, 6) -verify(h() == 39) +vereq(h(), 39) print "9. free variable in method" @@ -141,9 +141,9 @@ def test(): return Test() t = test() -verify(t.test() == "var") -verify(t.method_and_var() == "method") -verify(t.actual_global() == "global") +vereq(t.test(), "var") +vereq(t.method_and_var(), "method") +vereq(t.actual_global(), "global") method_and_var = "var" class Test: @@ -158,9 +158,9 @@ class Test: return str(self) t = Test() -verify(t.test() == "var") -verify(t.method_and_var() == "method") -verify(t.actual_global() == "global") +vereq(t.test(), "var") +vereq(t.method_and_var(), "method") +vereq(t.actual_global(), "global") print "10. recursion" @@ -175,7 +175,7 @@ def f(x): else: raise ValueError, "x must be >= 0" -verify(f(6) == 720) +vereq(f(6), 720) print "11. unoptimized namespaces" @@ -252,24 +252,24 @@ print "12. lambdas" f1 = lambda x: lambda y: x + y inc = f1(1) plus10 = f1(10) -verify(inc(1) == 2) -verify(plus10(5) == 15) +vereq(inc(1), 2) +vereq(plus10(5), 15) f2 = lambda x: (lambda : lambda y: x + y)() inc = f2(1) plus10 = f2(10) -verify(inc(1) == 2) -verify(plus10(5) == 15) +vereq(inc(1), 2) +vereq(plus10(5), 15) f3 = lambda x: lambda y: global_x + y global_x = 1 inc = f3(None) -verify(inc(2) == 3) +vereq(inc(2), 3) f8 = lambda x, y, z: lambda a, b, c: lambda : z * (b + y) g = f8(1, 2, 3) h = g(2, 4, 6) -verify(h() == 18) +vereq(h(), 18) print "13. UnboundLocal" @@ -306,21 +306,21 @@ def makeReturner(*lst): return lst return returner -verify(makeReturner(1,2,3)() == (1,2,3)) +vereq(makeReturner(1,2,3)(), (1,2,3)) def makeReturner2(**kwargs): def returner(): return kwargs return returner -verify(makeReturner2(a=11)()['a'] == 11) +vereq(makeReturner2(a=11)()['a'], 11) def makeAddPair((a, b)): def addPair((c, d)): return (a + c, b + d) return addPair -verify(makeAddPair((1, 2))((100, 200)) == (101,202)) +vereq(makeAddPair((1, 2))((100, 200)), (101,202)) print "15. scope of global statements" # Examples posted by Samuele Pedroni to python-dev on 3/1/2001 @@ -337,8 +337,8 @@ def f(): return h() return i() return g() -verify(f() == 7) -verify(x == 7) +vereq(f(), 7) +vereq(x, 7) # II x = 7 @@ -352,8 +352,8 @@ def f(): return h() return i() return g() -verify(f() == 2) -verify(x == 7) +vereq(f(), 2) +vereq(x, 7) # III x = 7 @@ -368,8 +368,8 @@ def f(): return h() return i() return g() -verify(f() == 2) -verify(x == 2) +vereq(f(), 2) +vereq(x, 2) # IV x = 7 @@ -384,8 +384,25 @@ def f(): return h() return i() return g() -verify(f() == 2) -verify(x == 2) +vereq(f(), 2) +vereq(x, 2) + +# XXX what about global statements in class blocks? +# do they affect methods? + +x = 12 +class Global: + global x + x = 13 + def set(self, val): + x = val + def get(self): + return x + +g = Global() +vereq(g.get(), 13) +g.set(15) +vereq(g.get(), 13) print "16. check leaks" @@ -407,7 +424,7 @@ def f1(): for i in range(100): f1() -verify(Foo.count == 0) +vereq(Foo.count, 0) print "17. class and global" @@ -419,9 +436,9 @@ def test(x): return Foo() x = 0 -verify(test(6)(2) == 8) +vereq(test(6)(2), 8) x = -1 -verify(test(3)(2) == 5) +vereq(test(3)(2), 5) print "18. verify that locals() works" @@ -437,7 +454,7 @@ def f(x): d = f(2)(4) verify(d.has_key('h')) del d['h'] -verify(d == {'x': 2, 'y': 7, 'w': 6}) +vereq(d, {'x': 2, 'y': 7, 'w': 6}) print "19. var is bound and free in class" @@ -449,7 +466,7 @@ def f(x): return C inst = f(3)() -verify(inst.a == inst.m()) +vereq(inst.a, inst.m()) print "20. interaction with trace function" diff --git a/Makefile.pre.in b/Makefile.pre.in index 66bc88a..7a69a6b 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -216,10 +216,22 @@ PGOBJS= \ PGENOBJS= $(PGENMAIN) $(POBJS) $(PGOBJS) +########################################################################## +# AST +AST_H= $(srcdir)/Include/Python-ast.h +AST_C= $(srcdir)/Python/Python-ast.c +AST_ASDL= $(srcdir)/Parser/Python.asdl + +ASDLGEN_FILES= $(srcdir)/Parser/asdl.py $(srcdir)/Parser/asdl_c.py +# XXX Note that a build now requires Python exist before the build starts +ASDLGEN= $(srcdir)/Parser/asdl_c.py -h $(srcdir)/Include -c $(srcdir)/Python ########################################################################## # Python PYTHON_OBJS= \ + Python/Python-ast.o \ + Python/asdl.o \ + Python/ast.o \ Python/bltinmodule.o \ Python/exceptions.o \ Python/ceval.o \ @@ -265,6 +277,7 @@ OBJECT_OBJS= \ Objects/cellobject.o \ Objects/classobject.o \ Objects/cobject.o \ + Objects/codeobject.o \ Objects/complexobject.o \ Objects/descrobject.o \ Objects/enumobject.o \ @@ -457,8 +470,10 @@ Parser/metagrammar.o: $(srcdir)/Parser/metagrammar.c Parser/tokenizer_pgen.o: $(srcdir)/Parser/tokenizer.c +$(AST_H) $(AST_C): $(AST_ASDL) $(ASDLGEN_FILES) + $(ASDLGEN) $(AST_ASDL) -Python/compile.o Python/symtable.o: $(GRAMMAR_H) +Python/compile.o Python/symtable.o: $(GRAMMAR_H) $(AST_H) Python/getplatform.o: $(srcdir)/Python/getplatform.c $(CC) -c $(PY_CFLAGS) -DPLATFORM='"$(MACHDEP)"' -o $@ $(srcdir)/Python/getplatform.c @@ -474,12 +489,15 @@ Objects/unicodectype.o: $(srcdir)/Objects/unicodectype.c \ PYTHON_HEADERS= \ Include/Python.h \ + Include/Python-ast.h \ + Include/asdl.h \ Include/abstract.h \ Include/boolobject.h \ Include/bufferobject.h \ Include/ceval.h \ Include/classobject.h \ Include/cobject.h \ + Include/code.h \ Include/codecs.h \ Include/compile.h \ Include/complexobject.h \ diff --git a/Misc/ACKS b/Misc/ACKS index 58a2c86..b95902a 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -165,6 +165,7 @@ Eugene Dvurechenski Maxim Dzumanenko Hans Eckardt Grant Edwards +John Ehresman Andrew Eland Lance Ellinghaus David Ely diff --git a/Modules/_hotshot.c b/Modules/_hotshot.c index b75b1a32..0842728 100644 --- a/Modules/_hotshot.c +++ b/Modules/_hotshot.c @@ -3,6 +3,7 @@ */ #include "Python.h" +#include "code.h" #include "compile.h" #include "eval.h" #include "frameobject.h" diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c index 909a404..7a52aae 100644 --- a/Modules/symtablemodule.c +++ b/Modules/symtablemodule.c @@ -1,6 +1,8 @@ #include "Python.h" +#include "code.h" #include "compile.h" +#include "Python-ast.h" #include "symtable.h" static PyObject * @@ -64,9 +66,9 @@ init_symtable(void) PyModule_AddIntConstant(m, "DEF_IMPORT", DEF_IMPORT); PyModule_AddIntConstant(m, "DEF_BOUND", DEF_BOUND); - PyModule_AddIntConstant(m, "TYPE_FUNCTION", TYPE_FUNCTION); - PyModule_AddIntConstant(m, "TYPE_CLASS", TYPE_CLASS); - PyModule_AddIntConstant(m, "TYPE_MODULE", TYPE_MODULE); + PyModule_AddIntConstant(m, "TYPE_FUNCTION", FunctionBlock); + PyModule_AddIntConstant(m, "TYPE_CLASS", ClassBlock); + PyModule_AddIntConstant(m, "TYPE_MODULE", ModuleBlock); PyModule_AddIntConstant(m, "OPT_IMPORT_STAR", OPT_IMPORT_STAR); PyModule_AddIntConstant(m, "OPT_EXEC", OPT_EXEC); diff --git a/Objects/codeobject.c b/Objects/codeobject.c new file mode 100644 index 0000000..c5ddfd5 --- /dev/null +++ b/Objects/codeobject.c @@ -0,0 +1,453 @@ +#include "Python.h" +#include "code.h" +#include "structmember.h" + +#define NAME_CHARS \ + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" + +/* all_name_chars(s): true iff all chars in s are valid NAME_CHARS */ + +static int +all_name_chars(unsigned char *s) +{ + static char ok_name_char[256]; + static unsigned char *name_chars = (unsigned char *)NAME_CHARS; + + if (ok_name_char[*name_chars] == 0) { + unsigned char *p; + for (p = name_chars; *p; p++) + ok_name_char[*p] = 1; + } + while (*s) { + if (ok_name_char[*s++] == 0) + return 0; + } + return 1; +} + +static void +intern_strings(PyObject *tuple) +{ + int i; + + for (i = PyTuple_GET_SIZE(tuple); --i >= 0; ) { + PyObject *v = PyTuple_GET_ITEM(tuple, i); + if (v == NULL || !PyString_CheckExact(v)) { + Py_FatalError("non-string found in code slot"); + } + PyString_InternInPlace(&PyTuple_GET_ITEM(tuple, i)); + } +} + + +PyCodeObject * +PyCode_New(int argcount, int nlocals, int stacksize, int flags, + PyObject *code, PyObject *consts, PyObject *names, + PyObject *varnames, PyObject *freevars, PyObject *cellvars, + PyObject *filename, PyObject *name, int firstlineno, + PyObject *lnotab) +{ + PyCodeObject *co; + int i; + /* Check argument types */ + if (argcount < 0 || nlocals < 0 || + code == NULL || + consts == NULL || !PyTuple_Check(consts) || + names == NULL || !PyTuple_Check(names) || + varnames == NULL || !PyTuple_Check(varnames) || + freevars == NULL || !PyTuple_Check(freevars) || + cellvars == NULL || !PyTuple_Check(cellvars) || + name == NULL || !PyString_Check(name) || + filename == NULL || !PyString_Check(filename) || + lnotab == NULL || !PyString_Check(lnotab) || + !PyObject_CheckReadBuffer(code)) { + PyErr_BadInternalCall(); + return NULL; + } + intern_strings(names); + intern_strings(varnames); + intern_strings(freevars); + intern_strings(cellvars); + /* Intern selected string constants */ + for (i = PyTuple_Size(consts); --i >= 0; ) { + PyObject *v = PyTuple_GetItem(consts, i); + if (!PyString_Check(v)) + continue; + if (!all_name_chars((unsigned char *)PyString_AS_STRING(v))) + continue; + PyString_InternInPlace(&PyTuple_GET_ITEM(consts, i)); + } + co = PyObject_NEW(PyCodeObject, &PyCode_Type); + if (co != NULL) { + co->co_argcount = argcount; + co->co_nlocals = nlocals; + co->co_stacksize = stacksize; + co->co_flags = flags; + Py_INCREF(code); + co->co_code = code; + Py_INCREF(consts); + co->co_consts = consts; + Py_INCREF(names); + co->co_names = names; + Py_INCREF(varnames); + co->co_varnames = varnames; + Py_INCREF(freevars); + co->co_freevars = freevars; + Py_INCREF(cellvars); + co->co_cellvars = cellvars; + Py_INCREF(filename); + co->co_filename = filename; + Py_INCREF(name); + co->co_name = name; + co->co_firstlineno = firstlineno; + Py_INCREF(lnotab); + co->co_lnotab = lnotab; + } + return co; +} + + +#define OFF(x) offsetof(PyCodeObject, x) + +static PyMemberDef code_memberlist[] = { + {"co_argcount", T_INT, OFF(co_argcount), READONLY}, + {"co_nlocals", T_INT, OFF(co_nlocals), READONLY}, + {"co_stacksize",T_INT, OFF(co_stacksize), READONLY}, + {"co_flags", T_INT, OFF(co_flags), READONLY}, + {"co_code", T_OBJECT, OFF(co_code), READONLY}, + {"co_consts", T_OBJECT, OFF(co_consts), READONLY}, + {"co_names", T_OBJECT, OFF(co_names), READONLY}, + {"co_varnames", T_OBJECT, OFF(co_varnames), READONLY}, + {"co_freevars", T_OBJECT, OFF(co_freevars), READONLY}, + {"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY}, + {"co_filename", T_OBJECT, OFF(co_filename), READONLY}, + {"co_name", T_OBJECT, OFF(co_name), READONLY}, + {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, + {"co_lnotab", T_OBJECT, OFF(co_lnotab), READONLY}, + {NULL} /* Sentinel */ +}; + +/* Helper for code_new: return a shallow copy of a tuple that is + guaranteed to contain exact strings, by converting string subclasses + to exact strings and complaining if a non-string is found. */ +static PyObject* +validate_and_copy_tuple(PyObject *tup) +{ + PyObject *newtuple; + PyObject *item; + int i, len; + + len = PyTuple_GET_SIZE(tup); + newtuple = PyTuple_New(len); + if (newtuple == NULL) + return NULL; + + for (i = 0; i < len; i++) { + item = PyTuple_GET_ITEM(tup, i); + if (PyString_CheckExact(item)) { + Py_INCREF(item); + } + else if (!PyString_Check(item)) { + PyErr_Format( + PyExc_TypeError, + "name tuples must contain only " + "strings, not '%.500s'", + item->ob_type->tp_name); + Py_DECREF(newtuple); + return NULL; + } + else { + item = PyString_FromStringAndSize( + PyString_AS_STRING(item), + PyString_GET_SIZE(item)); + if (item == NULL) { + Py_DECREF(newtuple); + return NULL; + } + } + PyTuple_SET_ITEM(newtuple, i, item); + } + + return newtuple; +} + +PyDoc_STRVAR(code_doc, +"code(argcount, nlocals, stacksize, flags, codestring, constants, names,\n\ + varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])\n\ +\n\ +Create a code object. Not for the faint of heart."); + +static PyObject * +code_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + int argcount; + int nlocals; + int stacksize; + int flags; + PyObject *co = NULL; + PyObject *code; + PyObject *consts; + PyObject *names, *ournames = NULL; + PyObject *varnames, *ourvarnames = NULL; + PyObject *freevars = NULL, *ourfreevars = NULL; + PyObject *cellvars = NULL, *ourcellvars = NULL; + PyObject *filename; + PyObject *name; + int firstlineno; + PyObject *lnotab; + + if (!PyArg_ParseTuple(args, "iiiiSO!O!O!SSiS|O!O!:code", + &argcount, &nlocals, &stacksize, &flags, + &code, + &PyTuple_Type, &consts, + &PyTuple_Type, &names, + &PyTuple_Type, &varnames, + &filename, &name, + &firstlineno, &lnotab, + &PyTuple_Type, &freevars, + &PyTuple_Type, &cellvars)) + return NULL; + + if (argcount < 0) { + PyErr_SetString( + PyExc_ValueError, + "code: argcount must not be negative"); + goto cleanup; + } + + if (nlocals < 0) { + PyErr_SetString( + PyExc_ValueError, + "code: nlocals must not be negative"); + goto cleanup; + } + + ournames = validate_and_copy_tuple(names); + if (ournames == NULL) + goto cleanup; + ourvarnames = validate_and_copy_tuple(varnames); + if (ourvarnames == NULL) + goto cleanup; + if (freevars) + ourfreevars = validate_and_copy_tuple(freevars); + else + ourfreevars = PyTuple_New(0); + if (ourfreevars == NULL) + goto cleanup; + if (cellvars) + ourcellvars = validate_and_copy_tuple(cellvars); + else + ourcellvars = PyTuple_New(0); + if (ourcellvars == NULL) + goto cleanup; + + co = (PyObject *)PyCode_New(argcount, nlocals, stacksize, flags, + code, consts, ournames, ourvarnames, + ourfreevars, ourcellvars, filename, + name, firstlineno, lnotab); + cleanup: + Py_XDECREF(ournames); + Py_XDECREF(ourvarnames); + Py_XDECREF(ourfreevars); + Py_XDECREF(ourcellvars); + return co; +} + +static void +code_dealloc(PyCodeObject *co) +{ + Py_XDECREF(co->co_code); + Py_XDECREF(co->co_consts); + Py_XDECREF(co->co_names); + Py_XDECREF(co->co_varnames); + Py_XDECREF(co->co_freevars); + Py_XDECREF(co->co_cellvars); + Py_XDECREF(co->co_filename); + Py_XDECREF(co->co_name); + Py_XDECREF(co->co_lnotab); + PyObject_DEL(co); +} + +static PyObject * +code_repr(PyCodeObject *co) +{ + char buf[500]; + int lineno = -1; + char *filename = "???"; + char *name = "???"; + + if (co->co_firstlineno != 0) + lineno = co->co_firstlineno; + if (co->co_filename && PyString_Check(co->co_filename)) + filename = PyString_AS_STRING(co->co_filename); + if (co->co_name && PyString_Check(co->co_name)) + name = PyString_AS_STRING(co->co_name); + PyOS_snprintf(buf, sizeof(buf), + "", + name, co, filename, lineno); + return PyString_FromString(buf); +} + +static int +code_compare(PyCodeObject *co, PyCodeObject *cp) +{ + int cmp; + cmp = PyObject_Compare(co->co_name, cp->co_name); + if (cmp) return cmp; + cmp = co->co_argcount - cp->co_argcount; + if (cmp) goto normalize; + cmp = co->co_nlocals - cp->co_nlocals; + if (cmp) goto normalize; + cmp = co->co_flags - cp->co_flags; + if (cmp) goto normalize; + cmp = co->co_firstlineno - cp->co_firstlineno; + if (cmp) goto normalize; + cmp = PyObject_Compare(co->co_code, cp->co_code); + if (cmp) return cmp; + cmp = PyObject_Compare(co->co_consts, cp->co_consts); + if (cmp) return cmp; + cmp = PyObject_Compare(co->co_names, cp->co_names); + if (cmp) return cmp; + cmp = PyObject_Compare(co->co_varnames, cp->co_varnames); + if (cmp) return cmp; + cmp = PyObject_Compare(co->co_freevars, cp->co_freevars); + if (cmp) return cmp; + cmp = PyObject_Compare(co->co_cellvars, cp->co_cellvars); + return cmp; + + normalize: + if (cmp > 0) + return 1; + else if (cmp < 0) + return -1; + else + return 0; +} + +static long +code_hash(PyCodeObject *co) +{ + long h, h0, h1, h2, h3, h4, h5, h6; + h0 = PyObject_Hash(co->co_name); + if (h0 == -1) return -1; + h1 = PyObject_Hash(co->co_code); + if (h1 == -1) return -1; + h2 = PyObject_Hash(co->co_consts); + if (h2 == -1) return -1; + h3 = PyObject_Hash(co->co_names); + if (h3 == -1) return -1; + h4 = PyObject_Hash(co->co_varnames); + if (h4 == -1) return -1; + h5 = PyObject_Hash(co->co_freevars); + if (h5 == -1) return -1; + h6 = PyObject_Hash(co->co_cellvars); + if (h6 == -1) return -1; + h = h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^ h6 ^ + co->co_argcount ^ co->co_nlocals ^ co->co_flags; + if (h == -1) h = -2; + return h; +} + +/* XXX code objects need to participate in GC? */ + +PyTypeObject PyCode_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "code", + sizeof(PyCodeObject), + 0, + (destructor)code_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)code_compare, /* tp_compare */ + (reprfunc)code_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)code_hash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + code_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + code_memberlist, /* tp_members */ + 0, /* 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 */ + code_new, /* tp_new */ +}; + +/* All about c_lnotab. + +c_lnotab is an array of unsigned bytes disguised as a Python string. In -O +mode, SET_LINENO opcodes aren't generated, and bytecode offsets are mapped +to source code line #s (when needed for tracebacks) via c_lnotab instead. +The array is conceptually a list of + (bytecode offset increment, line number increment) +pairs. The details are important and delicate, best illustrated by example: + + byte code offset source code line number + 0 1 + 6 2 + 50 7 + 350 307 + 361 308 + +The first trick is that these numbers aren't stored, only the increments +from one row to the next (this doesn't really work, but it's a start): + + 0, 1, 6, 1, 44, 5, 300, 300, 11, 1 + +The second trick is that an unsigned byte can't hold negative values, or +values larger than 255, so (a) there's a deep assumption that byte code +offsets and their corresponding line #s both increase monotonically, and (b) +if at least one column jumps by more than 255 from one row to the next, more +than one pair is written to the table. In case #b, there's no way to know +from looking at the table later how many were written. That's the delicate +part. A user of c_lnotab desiring to find the source line number +corresponding to a bytecode address A should do something like this + + lineno = addr = 0 + for addr_incr, line_incr in c_lnotab: + addr += addr_incr + if addr > A: + return lineno + lineno += line_incr + +In order for this to work, when the addr field increments by more than 255, +the line # increment in each pair generated must be 0 until the remaining addr +increment is < 256. So, in the example above, com_set_lineno should not (as +was actually done until 2.2) expand 300, 300 to 255, 255, 45, 45, but to +255, 0, 45, 255, 0, 45. +*/ + +int +PyCode_Addr2Line(PyCodeObject *co, int addrq) +{ + int size = PyString_Size(co->co_lnotab) / 2; + unsigned char *p = (unsigned char*)PyString_AsString(co->co_lnotab); + int line = co->co_firstlineno; + int addr = 0; + while (--size >= 0) { + addr += *p++; + if (addr > addrq) + break; + line += *p++; + } + return line; +} diff --git a/Objects/frameobject.c b/Objects/frameobject.c index bc8cae9..8ebf500 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -3,6 +3,7 @@ #include "Python.h" +#include "code.h" #include "compile.h" #include "frameobject.h" #include "opcode.h" diff --git a/Objects/funcobject.c b/Objects/funcobject.c index c0c91c9..85cef6f 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -2,7 +2,7 @@ /* Function object implementation */ #include "Python.h" -#include "compile.h" +#include "code.h" #include "eval.h" #include "structmember.h" @@ -144,7 +144,9 @@ PyFunction_SetClosure(PyObject *op, PyObject *closure) Py_XINCREF(closure); } else { - PyErr_SetString(PyExc_SystemError, "non-tuple closure"); + PyErr_Format(PyExc_SystemError, + "expected tuple for closure, got '%.100s'", + closure->ob_type->tp_name); return -1; } Py_XDECREF(((PyFunctionObject *) op) -> func_closure); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 88c4a15..591c62b 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1737,20 +1737,14 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) goto bad_slots; for (i = j = 0; i < nslots; i++) { char *s; - char buffer[256]; tmp = PyTuple_GET_ITEM(slots, i); s = PyString_AS_STRING(tmp); if ((add_dict && strcmp(s, "__dict__") == 0) || (add_weak && strcmp(s, "__weakref__") == 0)) continue; - if (_Py_Mangle(PyString_AS_STRING(name), - PyString_AS_STRING(tmp), - buffer, sizeof(buffer))) - { - tmp = PyString_FromString(buffer); - } else { - Py_INCREF(tmp); - } + tmp =_Py_Mangle(name, tmp); + if (!tmp) + goto bad_slots; PyTuple_SET_ITEM(newslots, j, tmp); j++; } diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj index 945fd5c..ff9e48b 100644 --- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -3,6 +3,7 @@ ProjectType="Visual C++" Version="7.10" Name="pythoncore" + ProjectGUID="{7AFA1F0B-A8A1-455A-A832-BF263404BBEF}" SccProjectName="pythoncore" SccLocalPath=".."> @@ -478,6 +479,12 @@ + + + + @@ -802,10 +809,13 @@ + + + RelativePath="..\Objects\complexobject.c"> + RelativePath="..\PC\config.c"> + RelativePath="..\Modules\cPickle.c"> + RelativePath="..\Modules\cStringIO.c"> + RelativePath="..\Modules\datetimemodule.c"> + + - - + RelativePath="..\Objects\dictobject.c"> + RelativePath="..\PC\dl_nt.c"> + RelativePath="..\Python\dynload_win.c"> + RelativePath="..\Objects\enumobject.c"> + RelativePath="..\Modules\errnomodule.c"> + RelativePath="..\Python\errors.c"> + RelativePath="..\Python\exceptions.c"> + RelativePath="..\Objects\fileobject.c"> + RelativePath="..\Parser\firstsets.c"> + + + RelativePath="..\Objects\frameobject.c"> + RelativePath="..\Python\frozen.c"> + RelativePath="..\Objects\funcobject.c"> + RelativePath="..\Modules\functionalmodule.c"> + + @@ -2302,6 +2318,9 @@ + + @@ -2437,9 +2456,6 @@ - - @@ -2467,6 +2483,9 @@ + + @@ -2551,6 +2570,9 @@ RelativePath="..\Python\pystrtod.c"> + + @@ -2764,6 +2786,7 @@ @@ -2819,6 +2842,9 @@ +======= + RelativePath="..\Modules\sha256module.c"> +>>>>>>> 1.26.2.3 + RelativePath="..\Modules\sha512module.c"> + RelativePath="..\Modules\signalmodule.c"> + RelativePath="..\Objects\sliceobject.c"> + RelativePath="..\Objects\stringobject.c"> + RelativePath="..\Modules\stropmodule.c"> + RelativePath="..\Python\structmember.c"> + RelativePath="..\Modules\structmodule.c"> + RelativePath="..\Objects\structseq.c"> + + + + + + + + + + RelativePath="..\Python\symtable.c"> + RelativePath="..\Modules\symtablemodule.c"> + + + RelativePath="..\Python\thread.c"> + RelativePath="..\Modules\threadmodule.c"> + RelativePath="..\Modules\timemodule.c"> + + diff --git a/Parser/.cvsignore b/Parser/.cvsignore index a7dd8e6..f300915 100644 --- a/Parser/.cvsignore +++ b/Parser/.cvsignore @@ -1,3 +1,6 @@ Makefile pgen add2lib +asdl.pyc +asdl_c.pyc +spark.pyc diff --git a/Parser/Python.asdl b/Parser/Python.asdl new file mode 100644 index 0000000..0883d91 --- /dev/null +++ b/Parser/Python.asdl @@ -0,0 +1,107 @@ +-- ASDL's three builtin types are identifier, int, string + +module Python +{ + mod = Module(stmt* body) + | Interactive(stmt* body) + | Expression(expr body) + + -- not really an actual node but useful in Jython's typesystem. + | Suite(stmt* body) + + stmt = FunctionDef(identifier name, arguments args, + stmt* body, expr* decorators) + | ClassDef(identifier name, expr* bases, stmt* body) + | Return(expr? value) + + | Delete(expr* targets) + | Assign(expr* targets, expr value) + | AugAssign(expr target, operator op, expr value) + + -- not sure if bool is allowed, can always use int + | Print(expr? dest, expr* values, bool nl) + + -- use 'orelse' because else is a keyword in target languages + | For(expr target, expr iter, stmt* body, stmt* orelse) + | While(expr test, stmt* body, stmt* orelse) + | If(expr test, stmt* body, stmt* orelse) + + -- 'type' is a bad name + | Raise(expr? type, expr? inst, expr? tback) + | TryExcept(stmt* body, excepthandler* handlers, stmt* orelse) + | TryFinally(stmt* body, stmt* finalbody) + | Assert(expr test, expr? msg) + + | Import(alias* names) + | ImportFrom(identifier module, alias* names) + + -- Doesn't capture requirement that locals must be + -- defined if globals is + -- still supports use as a function! + | Exec(expr body, expr? globals, expr? locals) + + | Global(identifier* names) + | Expr(expr value) + | Pass | Break | Continue + + -- XXX Jython will be different + attributes (int lineno) + + -- BoolOp() can use left & right? + expr = BoolOp(boolop op, expr* values) + | BinOp(expr left, operator op, expr right) + | UnaryOp(unaryop op, expr operand) + | Lambda(arguments args, expr body) + | Dict(expr* keys, expr* values) + | ListComp(expr elt, comprehension* generators) + | GeneratorExp(expr elt, comprehension* generators) + | Yield(expr? value) + -- need sequences for compare to distinguish between + -- x < 4 < 3 and (x < 4) < 3 + | Compare(expr left, cmpop* ops, expr* comparators) + | Call(expr func, expr* args, keyword* keywords, + expr? starargs, expr? kwargs) + | Repr(expr value) + | Num(object n) -- a number as a PyObject. + | Str(string s) -- need to specify raw, unicode, etc? + -- other literals? bools? + + -- the following expression can appear in assignment context + | Attribute(expr value, identifier attr, expr_context ctx) + | Subscript(expr value, slice slice, expr_context ctx) + | Name(identifier id, expr_context ctx) + | List(expr* elts, expr_context ctx) + | Tuple(expr *elts, expr_context ctx) + + attributes (int lineno) + + expr_context = Load | Store | Del | AugLoad | AugStore | Param + + slice = Ellipsis | Slice(expr? lower, expr? upper, expr? step) + | ExtSlice(slice* dims) + | Index(expr value) + + boolop = And | Or + + operator = Add | Sub | Mult | Div | Mod | Pow | LShift + | RShift | BitOr | BitXor | BitAnd | FloorDiv + + unaryop = Invert | Not | UAdd | USub + + cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn + + comprehension = (expr target, expr iter, expr* ifs) + + -- not sure what to call the first argument for raise and except + + excepthandler = (expr? type, expr? name, stmt* body) + + arguments = (expr* args, identifier? vararg, + identifier? kwarg, expr* defaults) + + -- keyword arguments supplied to call + keyword = (identifier arg, expr value) + + -- import name with optional 'as' alias. + alias = (identifier name, identifier? asname) +} diff --git a/Parser/asdl.py b/Parser/asdl.py new file mode 100644 index 0000000..0db4e3b --- /dev/null +++ b/Parser/asdl.py @@ -0,0 +1,393 @@ +"""An implementation of the Zephyr Abstract Syntax Definition Language. + +See http://asdl.sourceforge.net/ and +http://www.cs.princeton.edu/~danwang/Papers/dsl97/dsl97-abstract.html. + +Only supports top level module decl, not view. I'm guessing that view +is intended to support the browser and I'm not interested in the +browser. +""" + +#__metaclass__ = type + +import os +import traceback + +import spark + +class Token: + # spark seems to dispatch in the parser based on a token's + # type attribute + def __init__(self, type, lineno): + self.type = type + self.lineno = lineno + + def __str__(self): + return self.type + + def __repr__(self): + return str(self) + +class Id(Token): + def __init__(self, value, lineno): + self.type = 'Id' + self.value = value + self.lineno = lineno + + def __str__(self): + return self.value + +class ASDLSyntaxError: + + def __init__(self, lineno, token=None, msg=None): + self.lineno = lineno + self.token = token + self.msg = msg + + def __str__(self): + if self.msg is None: + return "Error at '%s', line %d" % (self.token, self.lineno) + else: + return "%s, line %d" % (self.msg, self.lineno) + +class ASDLScanner(spark.GenericScanner, object): + + def tokenize(self, input): + self.rv = [] + self.lineno = 1 + super(ASDLScanner, self).tokenize(input) + return self.rv + + def t_id(self, s): + r"[\w\.]+" + # XXX doesn't distinguish upper vs. lower, which is + # significant for ASDL. + self.rv.append(Id(s, self.lineno)) + + def t_xxx(self, s): # not sure what this production means + r"<=" + self.rv.append(Token(s, self.lineno)) + + def t_punctuation(self, s): + r"[\{\}\*\=\|\(\)\,\?\:]" + self.rv.append(Token(s, self.lineno)) + + def t_comment(self, s): + r"\-\-[^\n]*" + pass + + def t_newline(self, s): + r"\n" + self.lineno += 1 + + def t_whitespace(self, s): + r"[ \t]+" + pass + + def t_default(self, s): + r" . +" + raise ValueError, "unmatched input: %s" % `s` + +class ASDLParser(spark.GenericParser, object): + def __init__(self): + super(ASDLParser, self).__init__("module") + + def typestring(self, tok): + return tok.type + + def error(self, tok): + raise ASDLSyntaxError(tok.lineno, tok) + + def p_module_0(self, (module, name, _0, _1)): + " module ::= Id Id { } " + if module.value != "module": + raise ASDLSyntaxError(module.lineno, + msg="expected 'module', found %s" % module) + return Module(name, None) + + def p_module(self, (module, name, _0, definitions, _1)): + " module ::= Id Id { definitions } " + if module.value != "module": + raise ASDLSyntaxError(module.lineno, + msg="expected 'module', found %s" % module) + return Module(name, definitions) + + def p_definition_0(self, (definition,)): + " definitions ::= definition " + return definition + + def p_definition_1(self, (definitions, definition)): + " definitions ::= definition definitions " + return definitions + definition + + def p_definition(self, (id, _, type)): + " definition ::= Id = type " + return [Type(id, type)] + + def p_type_0(self, (product,)): + " type ::= product " + return product + + def p_type_1(self, (sum,)): + " type ::= sum " + return Sum(sum) + + def p_type_2(self, (sum, id, _0, attributes, _1)): + " type ::= sum Id ( fields ) " + if id.value != "attributes": + raise ASDLSyntaxError(id.lineno, + msg="expected attributes, found %s" % id) + return Sum(sum, attributes) + + def p_product(self, (_0, fields, _1)): + " product ::= ( fields ) " + # XXX can't I just construct things in the right order? + fields.reverse() + return Product(fields) + + def p_sum_0(self, (constructor,)): + " sum ::= constructor """ + return [constructor] + + def p_sum_1(self, (constructor, _, sum)): + " sum ::= constructor | sum " + return [constructor] + sum + + def p_sum_2(self, (constructor, _, sum)): + " sum ::= constructor | sum " + return [constructor] + sum + + def p_constructor_0(self, (id,)): + " constructor ::= Id " + return Constructor(id) + + def p_constructor_1(self, (id, _0, fields, _1)): + " constructor ::= Id ( fields ) " + # XXX can't I just construct things in the right order? + fields.reverse() + return Constructor(id, fields) + + def p_fields_0(self, (field,)): + " fields ::= field " + return [field] + + def p_fields_1(self, (field, _, fields)): + " fields ::= field , fields " + return fields + [field] + + def p_field_0(self, (type,)): + " field ::= Id " + return Field(type) + + def p_field_1(self, (type, name)): + " field ::= Id Id " + return Field(type, name) + + def p_field_2(self, (type, _, name)): + " field ::= Id * Id " + return Field(type, name, seq=1) + + def p_field_3(self, (type, _, name)): + " field ::= Id ? Id " + return Field(type, name, opt=1) + + def p_field_4(self, (type, _)): + " field ::= Id * " + return Field(type, seq=1) + + def p_field_5(self, (type, _)): + " field ::= Id ? " + return Field(type, opt=1) + +builtin_types = ("identifier", "string", "int", "bool", "object") + +# below is a collection of classes to capture the AST of an AST :-) +# not sure if any of the methods are useful yet, but I'm adding them +# piecemeal as they seem helpful + +class AST: + pass # a marker class + +class Module(AST): + def __init__(self, name, dfns): + self.name = name + self.dfns = dfns + self.types = {} # maps type name to value (from dfns) + for type in dfns: + self.types[type.name.value] = type.value + + def __repr__(self): + return "Module(%s, %s)" % (self.name, self.dfns) + +class Type(AST): + def __init__(self, name, value): + self.name = name + self.value = value + + def __repr__(self): + return "Type(%s, %s)" % (self.name, self.value) + +class Constructor(AST): + def __init__(self, name, fields=None): + self.name = name + self.fields = fields or [] + + def __repr__(self): + return "Constructor(%s, %s)" % (self.name, self.fields) + +class Field(AST): + def __init__(self, type, name=None, seq=0, opt=0): + self.type = type + self.name = name + self.seq = seq + self.opt = opt + + def __repr__(self): + if self.seq: + extra = ", seq=1" + elif self.opt: + extra = ", opt=1" + else: + extra = "" + if self.name is None: + return "Field(%s%s)" % (self.type, extra) + else: + return "Field(%s, %s%s)" % (self.type, self.name, extra) + +class Sum(AST): + def __init__(self, types, attributes=None): + self.types = types + self.attributes = attributes or [] + + def __repr__(self): + if self.attributes is None: + return "Sum(%s)" % self.types + else: + return "Sum(%s, %s)" % (self.types, self.attributes) + +class Product(AST): + def __init__(self, fields): + self.fields = fields + + def __repr__(self): + return "Product(%s)" % self.fields + +class VisitorBase(object): + + def __init__(self, skip=0): + self.cache = {} + self.skip = skip + + def visit(self, object, *args): + meth = self._dispatch(object) + if meth is None: + return + try: + meth(object, *args) + except Exception, err: + print "Error visiting", repr(object) + print err + traceback.print_exc() + # XXX hack + if hasattr(self, 'file'): + self.file.flush() + os._exit(1) + + def _dispatch(self, object): + assert isinstance(object, AST), repr(object) + klass = object.__class__ + meth = self.cache.get(klass) + if meth is None: + methname = "visit" + klass.__name__ + if self.skip: + meth = getattr(self, methname, None) + else: + meth = getattr(self, methname) + self.cache[klass] = meth + return meth + +class Check(VisitorBase): + + def __init__(self): + super(Check, self).__init__(skip=1) + self.cons = {} + self.errors = 0 + self.types = {} + + def visitModule(self, mod): + for dfn in mod.dfns: + self.visit(dfn) + + def visitType(self, type): + self.visit(type.value, str(type.name)) + + def visitSum(self, sum, name): + for t in sum.types: + self.visit(t, name) + + def visitConstructor(self, cons, name): + key = str(cons.name) + conflict = self.cons.get(key) + if conflict is None: + self.cons[key] = name + else: + print "Redefinition of constructor %s" % key + print "Defined in %s and %s" % (conflict, name) + self.errors += 1 + for f in cons.fields: + self.visit(f, key) + + def visitField(self, field, name): + key = str(field.type) + l = self.types.setdefault(key, []) + l.append(name) + + def visitProduct(self, prod, name): + for f in prod.fields: + self.visit(f, name) + +def check(mod): + v = Check() + v.visit(mod) + + for t in v.types: + if not mod.types.has_key(t) and not t in builtin_types: + v.errors += 1 + uses = ", ".join(v.types[t]) + print "Undefined type %s, used in %s" % (t, uses) + + return not v.errors + +def parse(file): + scanner = ASDLScanner() + parser = ASDLParser() + + buf = open(file).read() + tokens = scanner.tokenize(buf) + try: + return parser.parse(tokens) + except ASDLSyntaxError, err: + print err + lines = buf.split("\n") + print lines[err.lineno - 1] # lines starts at 0, files at 1 + +if __name__ == "__main__": + import glob + import sys + + if len(sys.argv) > 1: + files = sys.argv[1:] + else: + testdir = "tests" + files = glob.glob(testdir + "/*.asdl") + + for file in files: + print file + mod = parse(file) + print "module", mod.name + print len(mod.dfns), "definitions" + if not check(mod): + print "Check failed" + else: + for dfn in mod.dfns: + print dfn.type diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py new file mode 100755 index 0000000..7c6df4e --- /dev/null +++ b/Parser/asdl_c.py @@ -0,0 +1,621 @@ +#! /usr/bin/env python +"""Generate C code from an ASDL description.""" + +# TO DO +# handle fields that have a type but no name + +import os, sys, traceback + +import asdl + +TABSIZE = 8 +MAX_COL = 80 + +def get_c_type(name): + """Return a string for the C name of the type. + + This function special cases the default types provided by asdl: + identifier, string, int, bool. + """ + # XXX ack! need to figure out where Id is useful and where string + if isinstance(name, asdl.Id): + name = name.value + if name in asdl.builtin_types: + return name + else: + return "%s_ty" % name + +def reflow_lines(s, depth): + """Reflow the line s indented depth tabs. + + Return a sequence of lines where no line extends beyond MAX_COL + when properly indented. The first line is properly indented based + exclusively on depth * TABSIZE. All following lines -- these are + the reflowed lines generated by this function -- start at the same + column as the first character beyond the opening { in the first + line. + """ + size = MAX_COL - depth * TABSIZE + if len(s) < size: + return [s] + + lines = [] + cur = s + padding = "" + while len(cur) > size: + i = cur.rfind(' ', 0, size) + # XXX this should be fixed for real + if i == -1 and 'GeneratorExp' in cur: + i = size + 3 + assert i != -1, "Impossible line %d to reflow: %s" % (size, `s`) + lines.append(padding + cur[:i]) + if len(lines) == 1: + # find new size based on brace + j = cur.find('{', 0, i) + if j >= 0: + j += 2 # account for the brace and the space after it + size -= j + padding = " " * j + else: + j = cur.find('(', 0, i) + if j >= 0: + j += 1 # account for the paren (no space after it) + size -= j + padding = " " * j + cur = cur[i+1:] + else: + lines.append(padding + cur) + return lines + +def is_simple(sum): + """Return True if a sum is a simple. + + A sum is simple if its types have no fields, e.g. + unaryop = Invert | Not | UAdd | USub + """ + + for t in sum.types: + if t.fields: + return False + return True + +class EmitVisitor(asdl.VisitorBase): + """Visit that emits lines""" + + def __init__(self, file): + self.file = file + super(EmitVisitor, self).__init__() + + def emit(self, s, depth, reflow=1): + # XXX reflow long lines? + if reflow: + lines = reflow_lines(s, depth) + else: + lines = [s] + for line in lines: + line = (" " * TABSIZE * depth) + line + "\n" + self.file.write(line) + +class TypeDefVisitor(EmitVisitor): + def visitModule(self, mod): + for dfn in mod.dfns: + self.visit(dfn) + + def visitType(self, type, depth=0): + self.visit(type.value, type.name, depth) + + def visitSum(self, sum, name, depth): + if is_simple(sum): + self.simple_sum(sum, name, depth) + else: + self.sum_with_constructors(sum, name, depth) + + def simple_sum(self, sum, name, depth): + enum = [] + for i in range(len(sum.types)): + type = sum.types[i] + enum.append("%s=%d" % (type.name, i + 1)) + enums = ", ".join(enum) + ctype = get_c_type(name) + s = "typedef enum _%s { %s } %s;" % (name, enums, ctype) + self.emit(s, depth) + self.emit("", depth) + + def sum_with_constructors(self, sum, name, depth): + ctype = get_c_type(name) + s = "typedef struct _%(name)s *%(ctype)s;" % locals() + self.emit(s, depth) + self.emit("", depth) + + def visitProduct(self, product, name, depth): + ctype = get_c_type(name) + s = "typedef struct _%(name)s *%(ctype)s;" % locals() + self.emit(s, depth) + self.emit("", depth) + +class StructVisitor(EmitVisitor): + """Visitor to generate typdefs for AST.""" + + def visitModule(self, mod): + for dfn in mod.dfns: + self.visit(dfn) + + def visitType(self, type, depth=0): + self.visit(type.value, type.name, depth) + + def visitSum(self, sum, name, depth): + if not is_simple(sum): + self.sum_with_constructors(sum, name, depth) + + def sum_with_constructors(self, sum, name, depth): + def emit(s, depth=depth): + self.emit(s % sys._getframe(1).f_locals, depth) + enum = [] + for i in range(len(sum.types)): + type = sum.types[i] + enum.append("%s_kind=%d" % (type.name, i + 1)) + + emit("struct _%(name)s {") + emit("enum { " + ", ".join(enum) + " } kind;", depth + 1) + emit("union {", depth + 1) + for t in sum.types: + self.visit(t, depth + 2) + emit("} v;", depth + 1) + for field in sum.attributes: + # rudimentary attribute handling + type = str(field.type) + assert type in asdl.builtin_types, type + emit("%s %s;" % (type, field.name), depth + 1); + emit("};") + emit("") + + def visitConstructor(self, cons, depth): + if cons.fields: + self.emit("struct {", depth) + for f in cons.fields: + self.visit(f, depth + 1) + self.emit("} %s;" % cons.name, depth) + self.emit("", depth) + else: + # XXX not sure what I want here, nothing is probably fine + pass + + def visitField(self, field, depth): + # XXX need to lookup field.type, because it might be something + # like a builtin... + ctype = get_c_type(field.type) + name = field.name + if field.seq: + self.emit("asdl_seq *%(name)s;" % locals(), depth) + else: + self.emit("%(ctype)s %(name)s;" % locals(), depth) + + def visitProduct(self, product, name, depth): + self.emit("struct _%(name)s {" % locals(), depth) + for f in product.fields: + self.visit(f, depth + 1) + self.emit("};", depth) + self.emit("", depth) + +class PrototypeVisitor(EmitVisitor): + """Generate function prototypes for the .h file""" + + def visitModule(self, mod): + for dfn in mod.dfns: + self.visit(dfn) + + def visitType(self, type): + self.visit(type.value, type.name) + + def visitSum(self, sum, name): + if is_simple(sum): + pass # XXX + else: + for t in sum.types: + self.visit(t, name, sum.attributes) + + def get_args(self, fields): + """Return list of C argument into, one for each field. + + Argument info is 3-tuple of a C type, variable name, and flag + that is true if type can be NULL. + """ + args = [] + unnamed = {} + for f in fields: + if f.name is None: + name = f.type + c = unnamed[name] = unnamed.get(name, 0) + 1 + if c > 1: + name = "name%d" % (c - 1) + else: + name = f.name + # XXX should extend get_c_type() to handle this + if f.seq: + ctype = "asdl_seq *" + else: + ctype = get_c_type(f.type) + args.append((ctype, name, f.opt or f.seq)) + return args + + def visitConstructor(self, cons, type, attrs): + args = self.get_args(cons.fields) + attrs = self.get_args(attrs) + ctype = get_c_type(type) + self.emit_function(cons.name, ctype, args, attrs) + + def emit_function(self, name, ctype, args, attrs, union=1): + args = args + attrs + if args: + argstr = ", ".join(["%s %s" % (atype, aname) + for atype, aname, opt in args]) + else: + argstr = "void" + self.emit("%s %s(%s);" % (ctype, name, argstr), 0) + + def visitProduct(self, prod, name): + self.emit_function(name, get_c_type(name), + self.get_args(prod.fields), [], union=0) + +class FunctionVisitor(PrototypeVisitor): + """Visitor to generate constructor functions for AST.""" + + def emit_function(self, name, ctype, args, attrs, union=1): + def emit(s, depth=0, reflow=1): + self.emit(s, depth, reflow) + argstr = ", ".join(["%s %s" % (atype, aname) + for atype, aname, opt in args + attrs]) + self.emit("%s" % ctype, 0) + emit("%s(%s)" % (name, argstr)) + emit("{") + emit("%s p;" % ctype, 1) + for argtype, argname, opt in args: + # XXX hack alert: false is allowed for a bool + if not opt and not argtype == "bool": + emit("if (!%s) {" % argname, 1) + emit("PyErr_SetString(PyExc_ValueError,", 2) + msg = "field %s is required for %s" % (argname, name) + emit(' "%s");' % msg, + 2, reflow=0) + emit('return NULL;', 2) + emit('}', 1) + + emit("p = (%s)malloc(sizeof(*p));" % ctype, 1) + emit("if (!p) {", 1) + emit("PyErr_SetString(PyExc_MemoryError, \"no memory\");", 2) + emit("return NULL;", 2) + emit("}", 1) + if union: + self.emit_body_union(name, args, attrs) + else: + self.emit_body_struct(name, args, attrs) + emit("return p;", 1) + emit("}") + emit("") + + def emit_body_union(self, name, args, attrs): + def emit(s, depth=0, reflow=1): + self.emit(s, depth, reflow) + emit("p->kind = %s_kind;" % name, 1) + for argtype, argname, opt in args: + emit("p->v.%s.%s = %s;" % (name, argname, argname), 1) + for argtype, argname, opt in attrs: + emit("p->%s = %s;" % (argname, argname), 1) + + def emit_body_struct(self, name, args, attrs): + def emit(s, depth=0, reflow=1): + self.emit(s, depth, reflow) + for argtype, argname, opt in args: + emit("p->%s = %s;" % (argname, argname), 1) + assert not attrs + +class PickleVisitor(EmitVisitor): + + def visitModule(self, mod): + for dfn in mod.dfns: + self.visit(dfn) + + def visitType(self, type): + self.visit(type.value, type.name) + + def visitSum(self, sum, name): + pass + + def visitProduct(self, sum, name): + pass + + def visitConstructor(self, cons, name): + pass + + def visitField(self, sum): + pass + +class MarshalPrototypeVisitor(PickleVisitor): + + def prototype(self, sum, name): + ctype = get_c_type(name) + self.emit("int marshal_write_%s(PyObject **, int *, %s);" + % (name, ctype), 0) + + visitProduct = visitSum = prototype + +class FreePrototypeVisitor(PickleVisitor): + + def prototype(self, sum, name): + ctype = get_c_type(name) + self.emit("void free_%s(%s);" % (name, ctype), 0) + + visitProduct = visitSum = prototype + +_SPECIALIZED_SEQUENCES = ('stmt', 'expr') + +def find_sequence(fields, doing_specialization): + """Return True if any field uses a sequence.""" + for f in fields: + if f.seq: + if not doing_specialization: + return True + if str(f.type) not in _SPECIALIZED_SEQUENCES: + return True + return False + +def has_sequence(types, doing_specialization): + for t in types: + if find_sequence(t.fields, doing_specialization): + return True + return False + + +class StaticVisitor(PickleVisitor): + '''Very simple, always emit this static code''' + + CODE = '''static void +free_seq_exprs(asdl_seq *seq) +{ + int i, n; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_expr((expr_ty)asdl_seq_GET(seq, i)); + asdl_seq_free(seq); +} + +static void +free_seq_stmts(asdl_seq *seq) +{ + int i, n; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_stmt((stmt_ty)asdl_seq_GET(seq, i)); + asdl_seq_free(seq); +} +''' + + def visit(self, object): + self.emit(self.CODE, 0, reflow=False) + + +class FreeVisitor(PickleVisitor): + + def func_begin(self, name, has_seq): + ctype = get_c_type(name) + self.emit("void", 0) + self.emit("free_%s(%s o)" % (name, ctype), 0) + self.emit("{", 0) + if has_seq: + self.emit("int i, n;", 1) + self.emit("asdl_seq *seq;", 1) + self.emit('', 0) + self.emit('if (!o)', 1) + self.emit('return;', 2) + self.emit('', 0) + + def func_end(self): + self.emit("}", 0) + self.emit("", 0) + + def visitSum(self, sum, name): + has_seq = has_sequence(sum.types, True) + self.func_begin(name, has_seq) + if not is_simple(sum): + self.emit("switch (o->kind) {", 1) + for i in range(len(sum.types)): + t = sum.types[i] + self.visitConstructor(t, i + 1, name) + self.emit("}", 1) + self.emit("", 0) + self.emit("free(o);", 1) + self.func_end() + + def visitProduct(self, prod, name): + self.func_begin(name, find_sequence(prod.fields, True)) + for field in prod.fields: + self.visitField(field, name, 1, True) + self.emit("", 0) + self.emit("free(o);", 1) + self.func_end() + + def visitConstructor(self, cons, enum, name): + self.emit("case %s_kind:" % cons.name, 1) + for f in cons.fields: + self.visitField(f, cons.name, 2, False) + self.emit("break;", 2) + + def visitField(self, field, name, depth, product): + def emit(s, d): + self.emit(s, depth + d) + if product: + value = "o->%s" % field.name + else: + value = "o->v.%s.%s" % (name, field.name) + if field.seq: + self.emitSeq(field, value, depth, emit) + + # XXX need to know the simple types in advance, so that we + # don't call free_TYPE() for them. + + elif field.opt: + emit("if (%s) {" % value, 0) + self.free(field, value, depth + 1) + emit("}", 0) + else: + self.free(field, value, depth) + + def emitSeq(self, field, value, depth, emit): + # specialize for freeing sequences of statements and expressions + if str(field.type) in _SPECIALIZED_SEQUENCES: + c_code = "free_seq_%ss(%s);" % (field.type, value) + emit(c_code, 0) + else: + emit("seq = %s;" % value, 0) + emit("n = asdl_seq_LEN(seq);", 0) + emit("for (i = 0; i < n; i++)", 0) + self.free(field, "asdl_seq_GET(seq, i)", depth + 1) + emit("asdl_seq_free(seq);", 0) + + def free(self, field, value, depth): + if str(field.type) in ("identifier", "string", "object"): + ctype = get_c_type(field.type) + self.emit("Py_DECREF((%s)%s);" % (ctype, value), depth) + elif str(field.type) == "bool": + return + else: + ctype = get_c_type(field.type) + self.emit("free_%s((%s)%s);" % (field.type, ctype, value), depth) + + +class MarshalFunctionVisitor(PickleVisitor): + + def func_begin(self, name, has_seq): + ctype = get_c_type(name) + self.emit("int", 0) + self.emit("marshal_write_%s(PyObject **buf, int *off, %s o)" % + (name, ctype), 0) + self.emit("{", 0) + # XXX: add declaration of "int i;" properly + if has_seq or True: + self.emit("int i;", 1) # XXX only need it for sequences + + def func_end(self): + self.emit("return 1;", 1) + self.emit("}", 0) + self.emit("", 0) + + def visitSum(self, sum, name): + has_seq = has_sequence(sum.types, False) + self.func_begin(name, has_seq) + simple = is_simple(sum) + if simple: + self.emit("switch (o) {", 1) + else: + self.emit("switch (o->kind) {", 1) + for i in range(len(sum.types)): + t = sum.types[i] + self.visitConstructor(t, i + 1, name, simple) + self.emit("}", 1) + self.func_end() + + def visitProduct(self, prod, name): + self.func_begin(name, find_sequence(prod.fields, True)) + for field in prod.fields: + self.visitField(field, name, 1, 1) + self.func_end() + + def visitConstructor(self, cons, enum, name, simple): + if simple: + self.emit("case %s:" % cons.name, 1) + self.emit("marshal_write_int(buf, off, %d);" % enum, 2); + self.emit("break;", 2) + else: + self.emit("case %s_kind:" % cons.name, 1) + self.emit("marshal_write_int(buf, off, %d);" % enum, 2) + for f in cons.fields: + self.visitField(f, cons.name, 2, 0) + self.emit("break;", 2) + + def visitField(self, field, name, depth, product): + def emit(s, d): + self.emit(s, depth + d) + if product: + value = "o->%s" % field.name + else: + value = "o->v.%s.%s" % (name, field.name) + if field.seq: + emit("marshal_write_int(buf, off, asdl_seq_LEN(%s));" % value, 0) + emit("for (i = 0; i < asdl_seq_LEN(%s); i++) {" % value, 0) + emit("void *elt = asdl_seq_GET(%s, i);" % value, 1); + ctype = get_c_type(field.type); + emit("marshal_write_%s(buf, off, (%s)elt);" % (field.type, + ctype), 1) + emit("}", 0) + elif field.opt: + emit("if (%s) {" % value, 0) + emit("marshal_write_int(buf, off, 1);", 1) + emit("marshal_write_%s(buf, off, %s);" % (field.type, value), 1) + emit("}", 0) + emit("else {", 0) + emit("marshal_write_int(buf, off, 0);", 1) + emit("}", 0) + else: + emit("marshal_write_%s(buf, off, %s);" % (field.type, value), 0) + +class ChainOfVisitors: + def __init__(self, *visitors): + self.visitors = visitors + + def visit(self, object): + for v in self.visitors: + v.visit(object) + +def main(srcfile): + auto_gen_msg = '/* File automatically generated by %s */\n' % sys.argv[0] + mod = asdl.parse(srcfile) + if not asdl.check(mod): + sys.exit(1) + if INC_DIR: + p = "%s/%s-ast.h" % (INC_DIR, mod.name) + else: + p = "%s-ast.h" % mod.name + f = open(p, "wb") + print >> f, auto_gen_msg + print >> f, '#include "asdl.h"\n' + c = ChainOfVisitors(TypeDefVisitor(f), + StructVisitor(f), + PrototypeVisitor(f), + FreePrototypeVisitor(f), + MarshalPrototypeVisitor(f), + ) + c.visit(mod) + f.close() + + if SRC_DIR: + p = "%s/%s-ast.c" % (SRC_DIR, mod.name) + else: + p = "%s-ast.c" % mod.name + f = open(p, "wb") + print >> f, auto_gen_msg + print >> f, '#include "Python.h"' + print >> f, '#include "%s-ast.h"' % mod.name + print >> f + v = ChainOfVisitors(FunctionVisitor(f), + StaticVisitor(f), + FreeVisitor(f), + MarshalFunctionVisitor(f), + ) + v.visit(mod) + f.close() + +if __name__ == "__main__": + import sys + import getopt + + INC_DIR = '' + SRC_DIR = '' + opts, args = getopt.getopt(sys.argv[1:], "h:c:") + for o, v in opts: + if o == '-h': + INC_DIR = v + if o == '-c': + SRC_DIR = v + if len(args) != 1: + print "Must specify single input file" + main(args[0]) diff --git a/Parser/grammar.mak b/Parser/grammar.mak index a6f1abe..55f028f 100644 --- a/Parser/grammar.mak +++ b/Parser/grammar.mak @@ -15,7 +15,7 @@ # particular case --pragma in PC\pyconfig.h, which demands that # python23.lib get linked in). -LIBS= ..\PCbuild\python23.lib +LIBS= ..\PCbuild\python25.lib CFLAGS= /I ..\Include /I ..\PC /D MS_NO_COREDLL /D PGEN /MD diff --git a/Parser/parsetok.c b/Parser/parsetok.c index 1d25437..11d2232 100644 --- a/Parser/parsetok.c +++ b/Parser/parsetok.c @@ -21,7 +21,7 @@ static void initerr(perrdetail *err_ret, const char* filename); node * PyParser_ParseString(const char *s, grammar *g, int start, perrdetail *err_ret) { - return PyParser_ParseStringFlags(s, g, start, err_ret, 0); + return PyParser_ParseStringFlagsFilename(s, NULL, g, start, err_ret, 0); } node * @@ -56,7 +56,6 @@ PyParser_ParseStringFlagsFilename(const char *s, const char *filename, return parsetok(tok, g, start, err_ret, flags); } - /* Parse input coming from a file. Return error code, print some errors. */ node * @@ -210,7 +209,7 @@ parsetok(struct tok_state *tok, grammar *g, int start, perrdetail *err_ret, } static void -initerr(perrdetail *err_ret, const char* filename) +initerr(perrdetail *err_ret, const char *filename) { err_ret->error = E_OK; err_ret->filename = filename; diff --git a/Parser/spark.py b/Parser/spark.py new file mode 100644 index 0000000..00d9733 --- /dev/null +++ b/Parser/spark.py @@ -0,0 +1,840 @@ +# Copyright (c) 1998-2002 John Aycock +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__version__ = 'SPARK-0.7 (pre-alpha-5)' + +import re +import sys +import string + +def _namelist(instance): + namelist, namedict, classlist = [], {}, [instance.__class__] + for c in classlist: + for b in c.__bases__: + classlist.append(b) + for name in c.__dict__.keys(): + if not namedict.has_key(name): + namelist.append(name) + namedict[name] = 1 + return namelist + +class GenericScanner: + def __init__(self, flags=0): + pattern = self.reflect() + self.re = re.compile(pattern, re.VERBOSE|flags) + + self.index2func = {} + for name, number in self.re.groupindex.items(): + self.index2func[number-1] = getattr(self, 't_' + name) + + def makeRE(self, name): + doc = getattr(self, name).__doc__ + rv = '(?P<%s>%s)' % (name[2:], doc) + return rv + + def reflect(self): + rv = [] + for name in _namelist(self): + if name[:2] == 't_' and name != 't_default': + rv.append(self.makeRE(name)) + + rv.append(self.makeRE('t_default')) + return string.join(rv, '|') + + def error(self, s, pos): + print "Lexical error at position %s" % pos + raise SystemExit + + def tokenize(self, s): + pos = 0 + n = len(s) + while pos < n: + m = self.re.match(s, pos) + if m is None: + self.error(s, pos) + + groups = m.groups() + for i in range(len(groups)): + if groups[i] and self.index2func.has_key(i): + self.index2func[i](groups[i]) + pos = m.end() + + def t_default(self, s): + r'( . | \n )+' + print "Specification error: unmatched input" + raise SystemExit + +# +# Extracted from GenericParser and made global so that [un]picking works. +# +class _State: + def __init__(self, stateno, items): + self.T, self.complete, self.items = [], [], items + self.stateno = stateno + +class GenericParser: + # + # An Earley parser, as per J. Earley, "An Efficient Context-Free + # Parsing Algorithm", CACM 13(2), pp. 94-102. Also J. C. Earley, + # "An Efficient Context-Free Parsing Algorithm", Ph.D. thesis, + # Carnegie-Mellon University, August 1968. New formulation of + # the parser according to J. Aycock, "Practical Earley Parsing + # and the SPARK Toolkit", Ph.D. thesis, University of Victoria, + # 2001, and J. Aycock and R. N. Horspool, "Practical Earley + # Parsing", unpublished paper, 2001. + # + + def __init__(self, start): + self.rules = {} + self.rule2func = {} + self.rule2name = {} + self.collectRules() + self.augment(start) + self.ruleschanged = 1 + + _NULLABLE = '\e_' + _START = 'START' + _BOF = '|-' + + # + # When pickling, take the time to generate the full state machine; + # some information is then extraneous, too. Unfortunately we + # can't save the rule2func map. + # + def __getstate__(self): + if self.ruleschanged: + # + # XXX - duplicated from parse() + # + self.computeNull() + self.newrules = {} + self.new2old = {} + self.makeNewRules() + self.ruleschanged = 0 + self.edges, self.cores = {}, {} + self.states = { 0: self.makeState0() } + self.makeState(0, self._BOF) + # + # XXX - should find a better way to do this.. + # + changes = 1 + while changes: + changes = 0 + for k, v in self.edges.items(): + if v is None: + state, sym = k + if self.states.has_key(state): + self.goto(state, sym) + changes = 1 + rv = self.__dict__.copy() + for s in self.states.values(): + del s.items + del rv['rule2func'] + del rv['nullable'] + del rv['cores'] + return rv + + def __setstate__(self, D): + self.rules = {} + self.rule2func = {} + self.rule2name = {} + self.collectRules() + start = D['rules'][self._START][0][1][1] # Blech. + self.augment(start) + D['rule2func'] = self.rule2func + D['makeSet'] = self.makeSet_fast + self.__dict__ = D + + # + # A hook for GenericASTBuilder and GenericASTMatcher. Mess + # thee not with this; nor shall thee toucheth the _preprocess + # argument to addRule. + # + def preprocess(self, rule, func): return rule, func + + def addRule(self, doc, func, _preprocess=1): + fn = func + rules = string.split(doc) + + index = [] + for i in range(len(rules)): + if rules[i] == '::=': + index.append(i-1) + index.append(len(rules)) + + for i in range(len(index)-1): + lhs = rules[index[i]] + rhs = rules[index[i]+2:index[i+1]] + rule = (lhs, tuple(rhs)) + + if _preprocess: + rule, fn = self.preprocess(rule, func) + + if self.rules.has_key(lhs): + self.rules[lhs].append(rule) + else: + self.rules[lhs] = [ rule ] + self.rule2func[rule] = fn + self.rule2name[rule] = func.__name__[2:] + self.ruleschanged = 1 + + def collectRules(self): + for name in _namelist(self): + if name[:2] == 'p_': + func = getattr(self, name) + doc = func.__doc__ + self.addRule(doc, func) + + def augment(self, start): + rule = '%s ::= %s %s' % (self._START, self._BOF, start) + self.addRule(rule, lambda args: args[1], 0) + + def computeNull(self): + self.nullable = {} + tbd = [] + + for rulelist in self.rules.values(): + lhs = rulelist[0][0] + self.nullable[lhs] = 0 + for rule in rulelist: + rhs = rule[1] + if len(rhs) == 0: + self.nullable[lhs] = 1 + continue + # + # We only need to consider rules which + # consist entirely of nonterminal symbols. + # This should be a savings on typical + # grammars. + # + for sym in rhs: + if not self.rules.has_key(sym): + break + else: + tbd.append(rule) + changes = 1 + while changes: + changes = 0 + for lhs, rhs in tbd: + if self.nullable[lhs]: + continue + for sym in rhs: + if not self.nullable[sym]: + break + else: + self.nullable[lhs] = 1 + changes = 1 + + def makeState0(self): + s0 = _State(0, []) + for rule in self.newrules[self._START]: + s0.items.append((rule, 0)) + return s0 + + def finalState(self, tokens): + # + # Yuck. + # + if len(self.newrules[self._START]) == 2 and len(tokens) == 0: + return 1 + start = self.rules[self._START][0][1][1] + return self.goto(1, start) + + def makeNewRules(self): + worklist = [] + for rulelist in self.rules.values(): + for rule in rulelist: + worklist.append((rule, 0, 1, rule)) + + for rule, i, candidate, oldrule in worklist: + lhs, rhs = rule + n = len(rhs) + while i < n: + sym = rhs[i] + if not self.rules.has_key(sym) or \ + not self.nullable[sym]: + candidate = 0 + i = i + 1 + continue + + newrhs = list(rhs) + newrhs[i] = self._NULLABLE+sym + newrule = (lhs, tuple(newrhs)) + worklist.append((newrule, i+1, + candidate, oldrule)) + candidate = 0 + i = i + 1 + else: + if candidate: + lhs = self._NULLABLE+lhs + rule = (lhs, rhs) + if self.newrules.has_key(lhs): + self.newrules[lhs].append(rule) + else: + self.newrules[lhs] = [ rule ] + self.new2old[rule] = oldrule + + def typestring(self, token): + return None + + def error(self, token): + print "Syntax error at or near `%s' token" % token + raise SystemExit + + def parse(self, tokens): + sets = [ [(1,0), (2,0)] ] + self.links = {} + + if self.ruleschanged: + self.computeNull() + self.newrules = {} + self.new2old = {} + self.makeNewRules() + self.ruleschanged = 0 + self.edges, self.cores = {}, {} + self.states = { 0: self.makeState0() } + self.makeState(0, self._BOF) + + for i in xrange(len(tokens)): + sets.append([]) + + if sets[i] == []: + break + self.makeSet(tokens[i], sets, i) + else: + sets.append([]) + self.makeSet(None, sets, len(tokens)) + + #_dump(tokens, sets, self.states) + + finalitem = (self.finalState(tokens), 0) + if finalitem not in sets[-2]: + if len(tokens) > 0: + self.error(tokens[i-1]) + else: + self.error(None) + + return self.buildTree(self._START, finalitem, + tokens, len(sets)-2) + + def isnullable(self, sym): + # + # For symbols in G_e only. If we weren't supporting 1.5, + # could just use sym.startswith(). + # + return self._NULLABLE == sym[0:len(self._NULLABLE)] + + def skip(self, (lhs, rhs), pos=0): + n = len(rhs) + while pos < n: + if not self.isnullable(rhs[pos]): + break + pos = pos + 1 + return pos + + def makeState(self, state, sym): + assert sym is not None + # + # Compute \epsilon-kernel state's core and see if + # it exists already. + # + kitems = [] + for rule, pos in self.states[state].items: + lhs, rhs = rule + if rhs[pos:pos+1] == (sym,): + kitems.append((rule, self.skip(rule, pos+1))) + core = kitems + + core.sort() + tcore = tuple(core) + if self.cores.has_key(tcore): + return self.cores[tcore] + # + # Nope, doesn't exist. Compute it and the associated + # \epsilon-nonkernel state together; we'll need it right away. + # + k = self.cores[tcore] = len(self.states) + K, NK = _State(k, kitems), _State(k+1, []) + self.states[k] = K + predicted = {} + + edges = self.edges + rules = self.newrules + for X in K, NK: + worklist = X.items + for item in worklist: + rule, pos = item + lhs, rhs = rule + if pos == len(rhs): + X.complete.append(rule) + continue + + nextSym = rhs[pos] + key = (X.stateno, nextSym) + if not rules.has_key(nextSym): + if not edges.has_key(key): + edges[key] = None + X.T.append(nextSym) + else: + edges[key] = None + if not predicted.has_key(nextSym): + predicted[nextSym] = 1 + for prule in rules[nextSym]: + ppos = self.skip(prule) + new = (prule, ppos) + NK.items.append(new) + # + # Problem: we know K needs generating, but we + # don't yet know about NK. Can't commit anything + # regarding NK to self.edges until we're sure. Should + # we delay committing on both K and NK to avoid this + # hacky code? This creates other problems.. + # + if X is K: + edges = {} + + if NK.items == []: + return k + + # + # Check for \epsilon-nonkernel's core. Unfortunately we + # need to know the entire set of predicted nonterminals + # to do this without accidentally duplicating states. + # + core = predicted.keys() + core.sort() + tcore = tuple(core) + if self.cores.has_key(tcore): + self.edges[(k, None)] = self.cores[tcore] + return k + + nk = self.cores[tcore] = self.edges[(k, None)] = NK.stateno + self.edges.update(edges) + self.states[nk] = NK + return k + + def goto(self, state, sym): + key = (state, sym) + if not self.edges.has_key(key): + # + # No transitions from state on sym. + # + return None + + rv = self.edges[key] + if rv is None: + # + # Target state isn't generated yet. Remedy this. + # + rv = self.makeState(state, sym) + self.edges[key] = rv + return rv + + def gotoT(self, state, t): + return [self.goto(state, t)] + + def gotoST(self, state, st): + rv = [] + for t in self.states[state].T: + if st == t: + rv.append(self.goto(state, t)) + return rv + + def add(self, set, item, i=None, predecessor=None, causal=None): + if predecessor is None: + if item not in set: + set.append(item) + else: + key = (item, i) + if item not in set: + self.links[key] = [] + set.append(item) + self.links[key].append((predecessor, causal)) + + def makeSet(self, token, sets, i): + cur, next = sets[i], sets[i+1] + + ttype = token is not None and self.typestring(token) or None + if ttype is not None: + fn, arg = self.gotoT, ttype + else: + fn, arg = self.gotoST, token + + for item in cur: + ptr = (item, i) + state, parent = item + add = fn(state, arg) + for k in add: + if k is not None: + self.add(next, (k, parent), i+1, ptr) + nk = self.goto(k, None) + if nk is not None: + self.add(next, (nk, i+1)) + + if parent == i: + continue + + for rule in self.states[state].complete: + lhs, rhs = rule + for pitem in sets[parent]: + pstate, pparent = pitem + k = self.goto(pstate, lhs) + if k is not None: + why = (item, i, rule) + pptr = (pitem, parent) + self.add(cur, (k, pparent), + i, pptr, why) + nk = self.goto(k, None) + if nk is not None: + self.add(cur, (nk, i)) + + def makeSet_fast(self, token, sets, i): + # + # Call *only* when the entire state machine has been built! + # It relies on self.edges being filled in completely, and + # then duplicates and inlines code to boost speed at the + # cost of extreme ugliness. + # + cur, next = sets[i], sets[i+1] + ttype = token is not None and self.typestring(token) or None + + for item in cur: + ptr = (item, i) + state, parent = item + if ttype is not None: + k = self.edges.get((state, ttype), None) + if k is not None: + #self.add(next, (k, parent), i+1, ptr) + #INLINED --v + new = (k, parent) + key = (new, i+1) + if new not in next: + self.links[key] = [] + next.append(new) + self.links[key].append((ptr, None)) + #INLINED --^ + #nk = self.goto(k, None) + nk = self.edges.get((k, None), None) + if nk is not None: + #self.add(next, (nk, i+1)) + #INLINED --v + new = (nk, i+1) + if new not in next: + next.append(new) + #INLINED --^ + else: + add = self.gotoST(state, token) + for k in add: + if k is not None: + self.add(next, (k, parent), i+1, ptr) + #nk = self.goto(k, None) + nk = self.edges.get((k, None), None) + if nk is not None: + self.add(next, (nk, i+1)) + + if parent == i: + continue + + for rule in self.states[state].complete: + lhs, rhs = rule + for pitem in sets[parent]: + pstate, pparent = pitem + #k = self.goto(pstate, lhs) + k = self.edges.get((pstate, lhs), None) + if k is not None: + why = (item, i, rule) + pptr = (pitem, parent) + #self.add(cur, (k, pparent), + # i, pptr, why) + #INLINED --v + new = (k, pparent) + key = (new, i) + if new not in cur: + self.links[key] = [] + cur.append(new) + self.links[key].append((pptr, why)) + #INLINED --^ + #nk = self.goto(k, None) + nk = self.edges.get((k, None), None) + if nk is not None: + #self.add(cur, (nk, i)) + #INLINED --v + new = (nk, i) + if new not in cur: + cur.append(new) + #INLINED --^ + + def predecessor(self, key, causal): + for p, c in self.links[key]: + if c == causal: + return p + assert 0 + + def causal(self, key): + links = self.links[key] + if len(links) == 1: + return links[0][1] + choices = [] + rule2cause = {} + for p, c in links: + rule = c[2] + choices.append(rule) + rule2cause[rule] = c + return rule2cause[self.ambiguity(choices)] + + def deriveEpsilon(self, nt): + if len(self.newrules[nt]) > 1: + rule = self.ambiguity(self.newrules[nt]) + else: + rule = self.newrules[nt][0] + #print rule + + rhs = rule[1] + attr = [None] * len(rhs) + + for i in range(len(rhs)-1, -1, -1): + attr[i] = self.deriveEpsilon(rhs[i]) + return self.rule2func[self.new2old[rule]](attr) + + def buildTree(self, nt, item, tokens, k): + state, parent = item + + choices = [] + for rule in self.states[state].complete: + if rule[0] == nt: + choices.append(rule) + rule = choices[0] + if len(choices) > 1: + rule = self.ambiguity(choices) + #print rule + + rhs = rule[1] + attr = [None] * len(rhs) + + for i in range(len(rhs)-1, -1, -1): + sym = rhs[i] + if not self.newrules.has_key(sym): + if sym != self._BOF: + attr[i] = tokens[k-1] + key = (item, k) + item, k = self.predecessor(key, None) + #elif self.isnullable(sym): + elif self._NULLABLE == sym[0:len(self._NULLABLE)]: + attr[i] = self.deriveEpsilon(sym) + else: + key = (item, k) + why = self.causal(key) + attr[i] = self.buildTree(sym, why[0], + tokens, why[1]) + item, k = self.predecessor(key, why) + return self.rule2func[self.new2old[rule]](attr) + + def ambiguity(self, rules): + # + # XXX - problem here and in collectRules() if the same rule + # appears in >1 method. Also undefined results if rules + # causing the ambiguity appear in the same method. + # + sortlist = [] + name2index = {} + for i in range(len(rules)): + lhs, rhs = rule = rules[i] + name = self.rule2name[self.new2old[rule]] + sortlist.append((len(rhs), name)) + name2index[name] = i + sortlist.sort() + list = map(lambda (a,b): b, sortlist) + return rules[name2index[self.resolve(list)]] + + def resolve(self, list): + # + # Resolve ambiguity in favor of the shortest RHS. + # Since we walk the tree from the top down, this + # should effectively resolve in favor of a "shift". + # + return list[0] + +# +# GenericASTBuilder automagically constructs a concrete/abstract syntax tree +# for a given input. The extra argument is a class (not an instance!) +# which supports the "__setslice__" and "__len__" methods. +# +# XXX - silently overrides any user code in methods. +# + +class GenericASTBuilder(GenericParser): + def __init__(self, AST, start): + GenericParser.__init__(self, start) + self.AST = AST + + def preprocess(self, rule, func): + rebind = lambda lhs, self=self: \ + lambda args, lhs=lhs, self=self: \ + self.buildASTNode(args, lhs) + lhs, rhs = rule + return rule, rebind(lhs) + + def buildASTNode(self, args, lhs): + children = [] + for arg in args: + if isinstance(arg, self.AST): + children.append(arg) + else: + children.append(self.terminal(arg)) + return self.nonterminal(lhs, children) + + def terminal(self, token): return token + + def nonterminal(self, type, args): + rv = self.AST(type) + rv[:len(args)] = args + return rv + +# +# GenericASTTraversal is a Visitor pattern according to Design Patterns. For +# each node it attempts to invoke the method n_, falling +# back onto the default() method if the n_* can't be found. The preorder +# traversal also looks for an exit hook named n__exit (no default +# routine is called if it's not found). To prematurely halt traversal +# of a subtree, call the prune() method -- this only makes sense for a +# preorder traversal. Node type is determined via the typestring() method. +# + +class GenericASTTraversalPruningException: + pass + +class GenericASTTraversal: + def __init__(self, ast): + self.ast = ast + + def typestring(self, node): + return node.type + + def prune(self): + raise GenericASTTraversalPruningException + + def preorder(self, node=None): + if node is None: + node = self.ast + + try: + name = 'n_' + self.typestring(node) + if hasattr(self, name): + func = getattr(self, name) + func(node) + else: + self.default(node) + except GenericASTTraversalPruningException: + return + + for kid in node: + self.preorder(kid) + + name = name + '_exit' + if hasattr(self, name): + func = getattr(self, name) + func(node) + + def postorder(self, node=None): + if node is None: + node = self.ast + + for kid in node: + self.postorder(kid) + + name = 'n_' + self.typestring(node) + if hasattr(self, name): + func = getattr(self, name) + func(node) + else: + self.default(node) + + + def default(self, node): + pass + +# +# GenericASTMatcher. AST nodes must have "__getitem__" and "__cmp__" +# implemented. +# +# XXX - makes assumptions about how GenericParser walks the parse tree. +# + +class GenericASTMatcher(GenericParser): + def __init__(self, start, ast): + GenericParser.__init__(self, start) + self.ast = ast + + def preprocess(self, rule, func): + rebind = lambda func, self=self: \ + lambda args, func=func, self=self: \ + self.foundMatch(args, func) + lhs, rhs = rule + rhslist = list(rhs) + rhslist.reverse() + + return (lhs, tuple(rhslist)), rebind(func) + + def foundMatch(self, args, func): + func(args[-1]) + return args[-1] + + def match_r(self, node): + self.input.insert(0, node) + children = 0 + + for child in node: + if children == 0: + self.input.insert(0, '(') + children = children + 1 + self.match_r(child) + + if children > 0: + self.input.insert(0, ')') + + def match(self, ast=None): + if ast is None: + ast = self.ast + self.input = [] + + self.match_r(ast) + self.parse(self.input) + + def resolve(self, list): + # + # Resolve ambiguity in favor of the longest RHS. + # + return list[-1] + +def _dump(tokens, sets, states): + for i in range(len(sets)): + print 'set', i + for item in sets[i]: + print '\t', item + for (lhs, rhs), pos in states[item[0]].items: + print '\t\t', lhs, '::=', + print string.join(rhs[:pos]), + print '.', + print string.join(rhs[pos:]) + if i < len(tokens): + print + print 'token', str(tokens[i]) + print diff --git a/Python/Python-ast.c b/Python/Python-ast.c new file mode 100644 index 0000000..e82b61c --- /dev/null +++ b/Python/Python-ast.c @@ -0,0 +1,2281 @@ +/* File automatically generated by ../Parser/asdl_c.py */ + +#include "Python.h" +#include "Python-ast.h" + +mod_ty +Module(asdl_seq * body) +{ + mod_ty p; + p = (mod_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Module_kind; + p->v.Module.body = body; + return p; +} + +mod_ty +Interactive(asdl_seq * body) +{ + mod_ty p; + p = (mod_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Interactive_kind; + p->v.Interactive.body = body; + return p; +} + +mod_ty +Expression(expr_ty body) +{ + mod_ty p; + if (!body) { + PyErr_SetString(PyExc_ValueError, + "field body is required for Expression"); + return NULL; + } + p = (mod_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Expression_kind; + p->v.Expression.body = body; + return p; +} + +mod_ty +Suite(asdl_seq * body) +{ + mod_ty p; + p = (mod_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Suite_kind; + p->v.Suite.body = body; + return p; +} + +stmt_ty +FunctionDef(identifier name, arguments_ty args, asdl_seq * body, asdl_seq * + decorators, int lineno) +{ + stmt_ty p; + if (!name) { + PyErr_SetString(PyExc_ValueError, + "field name is required for FunctionDef"); + return NULL; + } + if (!args) { + PyErr_SetString(PyExc_ValueError, + "field args is required for FunctionDef"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = FunctionDef_kind; + p->v.FunctionDef.name = name; + p->v.FunctionDef.args = args; + p->v.FunctionDef.body = body; + p->v.FunctionDef.decorators = decorators; + p->lineno = lineno; + return p; +} + +stmt_ty +ClassDef(identifier name, asdl_seq * bases, asdl_seq * body, int lineno) +{ + stmt_ty p; + if (!name) { + PyErr_SetString(PyExc_ValueError, + "field name is required for ClassDef"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = ClassDef_kind; + p->v.ClassDef.name = name; + p->v.ClassDef.bases = bases; + p->v.ClassDef.body = body; + p->lineno = lineno; + return p; +} + +stmt_ty +Return(expr_ty value, int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Return_kind; + p->v.Return.value = value; + p->lineno = lineno; + return p; +} + +stmt_ty +Delete(asdl_seq * targets, int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Delete_kind; + p->v.Delete.targets = targets; + p->lineno = lineno; + return p; +} + +stmt_ty +Assign(asdl_seq * targets, expr_ty value, int lineno) +{ + stmt_ty p; + if (!value) { + PyErr_SetString(PyExc_ValueError, + "field value is required for Assign"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Assign_kind; + p->v.Assign.targets = targets; + p->v.Assign.value = value; + p->lineno = lineno; + return p; +} + +stmt_ty +AugAssign(expr_ty target, operator_ty op, expr_ty value, int lineno) +{ + stmt_ty p; + if (!target) { + PyErr_SetString(PyExc_ValueError, + "field target is required for AugAssign"); + return NULL; + } + if (!op) { + PyErr_SetString(PyExc_ValueError, + "field op is required for AugAssign"); + return NULL; + } + if (!value) { + PyErr_SetString(PyExc_ValueError, + "field value is required for AugAssign"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = AugAssign_kind; + p->v.AugAssign.target = target; + p->v.AugAssign.op = op; + p->v.AugAssign.value = value; + p->lineno = lineno; + return p; +} + +stmt_ty +Print(expr_ty dest, asdl_seq * values, bool nl, int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Print_kind; + p->v.Print.dest = dest; + p->v.Print.values = values; + p->v.Print.nl = nl; + p->lineno = lineno; + return p; +} + +stmt_ty +For(expr_ty target, expr_ty iter, asdl_seq * body, asdl_seq * orelse, int + lineno) +{ + stmt_ty p; + if (!target) { + PyErr_SetString(PyExc_ValueError, + "field target is required for For"); + return NULL; + } + if (!iter) { + PyErr_SetString(PyExc_ValueError, + "field iter is required for For"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = For_kind; + p->v.For.target = target; + p->v.For.iter = iter; + p->v.For.body = body; + p->v.For.orelse = orelse; + p->lineno = lineno; + return p; +} + +stmt_ty +While(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno) +{ + stmt_ty p; + if (!test) { + PyErr_SetString(PyExc_ValueError, + "field test is required for While"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = While_kind; + p->v.While.test = test; + p->v.While.body = body; + p->v.While.orelse = orelse; + p->lineno = lineno; + return p; +} + +stmt_ty +If(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno) +{ + stmt_ty p; + if (!test) { + PyErr_SetString(PyExc_ValueError, + "field test is required for If"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = If_kind; + p->v.If.test = test; + p->v.If.body = body; + p->v.If.orelse = orelse; + p->lineno = lineno; + return p; +} + +stmt_ty +Raise(expr_ty type, expr_ty inst, expr_ty tback, int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Raise_kind; + p->v.Raise.type = type; + p->v.Raise.inst = inst; + p->v.Raise.tback = tback; + p->lineno = lineno; + return p; +} + +stmt_ty +TryExcept(asdl_seq * body, asdl_seq * handlers, asdl_seq * orelse, int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = TryExcept_kind; + p->v.TryExcept.body = body; + p->v.TryExcept.handlers = handlers; + p->v.TryExcept.orelse = orelse; + p->lineno = lineno; + return p; +} + +stmt_ty +TryFinally(asdl_seq * body, asdl_seq * finalbody, int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = TryFinally_kind; + p->v.TryFinally.body = body; + p->v.TryFinally.finalbody = finalbody; + p->lineno = lineno; + return p; +} + +stmt_ty +Assert(expr_ty test, expr_ty msg, int lineno) +{ + stmt_ty p; + if (!test) { + PyErr_SetString(PyExc_ValueError, + "field test is required for Assert"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Assert_kind; + p->v.Assert.test = test; + p->v.Assert.msg = msg; + p->lineno = lineno; + return p; +} + +stmt_ty +Import(asdl_seq * names, int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Import_kind; + p->v.Import.names = names; + p->lineno = lineno; + return p; +} + +stmt_ty +ImportFrom(identifier module, asdl_seq * names, int lineno) +{ + stmt_ty p; + if (!module) { + PyErr_SetString(PyExc_ValueError, + "field module is required for ImportFrom"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = ImportFrom_kind; + p->v.ImportFrom.module = module; + p->v.ImportFrom.names = names; + p->lineno = lineno; + return p; +} + +stmt_ty +Exec(expr_ty body, expr_ty globals, expr_ty locals, int lineno) +{ + stmt_ty p; + if (!body) { + PyErr_SetString(PyExc_ValueError, + "field body is required for Exec"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Exec_kind; + p->v.Exec.body = body; + p->v.Exec.globals = globals; + p->v.Exec.locals = locals; + p->lineno = lineno; + return p; +} + +stmt_ty +Global(asdl_seq * names, int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Global_kind; + p->v.Global.names = names; + p->lineno = lineno; + return p; +} + +stmt_ty +Expr(expr_ty value, int lineno) +{ + stmt_ty p; + if (!value) { + PyErr_SetString(PyExc_ValueError, + "field value is required for Expr"); + return NULL; + } + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Expr_kind; + p->v.Expr.value = value; + p->lineno = lineno; + return p; +} + +stmt_ty +Pass(int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Pass_kind; + p->lineno = lineno; + return p; +} + +stmt_ty +Break(int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Break_kind; + p->lineno = lineno; + return p; +} + +stmt_ty +Continue(int lineno) +{ + stmt_ty p; + p = (stmt_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Continue_kind; + p->lineno = lineno; + return p; +} + +expr_ty +BoolOp(boolop_ty op, asdl_seq * values, int lineno) +{ + expr_ty p; + if (!op) { + PyErr_SetString(PyExc_ValueError, + "field op is required for BoolOp"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = BoolOp_kind; + p->v.BoolOp.op = op; + p->v.BoolOp.values = values; + p->lineno = lineno; + return p; +} + +expr_ty +BinOp(expr_ty left, operator_ty op, expr_ty right, int lineno) +{ + expr_ty p; + if (!left) { + PyErr_SetString(PyExc_ValueError, + "field left is required for BinOp"); + return NULL; + } + if (!op) { + PyErr_SetString(PyExc_ValueError, + "field op is required for BinOp"); + return NULL; + } + if (!right) { + PyErr_SetString(PyExc_ValueError, + "field right is required for BinOp"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = BinOp_kind; + p->v.BinOp.left = left; + p->v.BinOp.op = op; + p->v.BinOp.right = right; + p->lineno = lineno; + return p; +} + +expr_ty +UnaryOp(unaryop_ty op, expr_ty operand, int lineno) +{ + expr_ty p; + if (!op) { + PyErr_SetString(PyExc_ValueError, + "field op is required for UnaryOp"); + return NULL; + } + if (!operand) { + PyErr_SetString(PyExc_ValueError, + "field operand is required for UnaryOp"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = UnaryOp_kind; + p->v.UnaryOp.op = op; + p->v.UnaryOp.operand = operand; + p->lineno = lineno; + return p; +} + +expr_ty +Lambda(arguments_ty args, expr_ty body, int lineno) +{ + expr_ty p; + if (!args) { + PyErr_SetString(PyExc_ValueError, + "field args is required for Lambda"); + return NULL; + } + if (!body) { + PyErr_SetString(PyExc_ValueError, + "field body is required for Lambda"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Lambda_kind; + p->v.Lambda.args = args; + p->v.Lambda.body = body; + p->lineno = lineno; + return p; +} + +expr_ty +Dict(asdl_seq * keys, asdl_seq * values, int lineno) +{ + expr_ty p; + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Dict_kind; + p->v.Dict.keys = keys; + p->v.Dict.values = values; + p->lineno = lineno; + return p; +} + +expr_ty +ListComp(expr_ty elt, asdl_seq * generators, int lineno) +{ + expr_ty p; + if (!elt) { + PyErr_SetString(PyExc_ValueError, + "field elt is required for ListComp"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = ListComp_kind; + p->v.ListComp.elt = elt; + p->v.ListComp.generators = generators; + p->lineno = lineno; + return p; +} + +expr_ty +GeneratorExp(expr_ty elt, asdl_seq * generators, int lineno) +{ + expr_ty p; + if (!elt) { + PyErr_SetString(PyExc_ValueError, + "field elt is required for GeneratorExp"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = GeneratorExp_kind; + p->v.GeneratorExp.elt = elt; + p->v.GeneratorExp.generators = generators; + p->lineno = lineno; + return p; +} + +expr_ty +Yield(expr_ty value, int lineno) +{ + expr_ty p; + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Yield_kind; + p->v.Yield.value = value; + p->lineno = lineno; + return p; +} + +expr_ty +Compare(expr_ty left, asdl_seq * ops, asdl_seq * comparators, int lineno) +{ + expr_ty p; + if (!left) { + PyErr_SetString(PyExc_ValueError, + "field left is required for Compare"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Compare_kind; + p->v.Compare.left = left; + p->v.Compare.ops = ops; + p->v.Compare.comparators = comparators; + p->lineno = lineno; + return p; +} + +expr_ty +Call(expr_ty func, asdl_seq * args, asdl_seq * keywords, expr_ty starargs, + expr_ty kwargs, int lineno) +{ + expr_ty p; + if (!func) { + PyErr_SetString(PyExc_ValueError, + "field func is required for Call"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Call_kind; + p->v.Call.func = func; + p->v.Call.args = args; + p->v.Call.keywords = keywords; + p->v.Call.starargs = starargs; + p->v.Call.kwargs = kwargs; + p->lineno = lineno; + return p; +} + +expr_ty +Repr(expr_ty value, int lineno) +{ + expr_ty p; + if (!value) { + PyErr_SetString(PyExc_ValueError, + "field value is required for Repr"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Repr_kind; + p->v.Repr.value = value; + p->lineno = lineno; + return p; +} + +expr_ty +Num(object n, int lineno) +{ + expr_ty p; + if (!n) { + PyErr_SetString(PyExc_ValueError, + "field n is required for Num"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Num_kind; + p->v.Num.n = n; + p->lineno = lineno; + return p; +} + +expr_ty +Str(string s, int lineno) +{ + expr_ty p; + if (!s) { + PyErr_SetString(PyExc_ValueError, + "field s is required for Str"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Str_kind; + p->v.Str.s = s; + p->lineno = lineno; + return p; +} + +expr_ty +Attribute(expr_ty value, identifier attr, expr_context_ty ctx, int lineno) +{ + expr_ty p; + if (!value) { + PyErr_SetString(PyExc_ValueError, + "field value is required for Attribute"); + return NULL; + } + if (!attr) { + PyErr_SetString(PyExc_ValueError, + "field attr is required for Attribute"); + return NULL; + } + if (!ctx) { + PyErr_SetString(PyExc_ValueError, + "field ctx is required for Attribute"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Attribute_kind; + p->v.Attribute.value = value; + p->v.Attribute.attr = attr; + p->v.Attribute.ctx = ctx; + p->lineno = lineno; + return p; +} + +expr_ty +Subscript(expr_ty value, slice_ty slice, expr_context_ty ctx, int lineno) +{ + expr_ty p; + if (!value) { + PyErr_SetString(PyExc_ValueError, + "field value is required for Subscript"); + return NULL; + } + if (!slice) { + PyErr_SetString(PyExc_ValueError, + "field slice is required for Subscript"); + return NULL; + } + if (!ctx) { + PyErr_SetString(PyExc_ValueError, + "field ctx is required for Subscript"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Subscript_kind; + p->v.Subscript.value = value; + p->v.Subscript.slice = slice; + p->v.Subscript.ctx = ctx; + p->lineno = lineno; + return p; +} + +expr_ty +Name(identifier id, expr_context_ty ctx, int lineno) +{ + expr_ty p; + if (!id) { + PyErr_SetString(PyExc_ValueError, + "field id is required for Name"); + return NULL; + } + if (!ctx) { + PyErr_SetString(PyExc_ValueError, + "field ctx is required for Name"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Name_kind; + p->v.Name.id = id; + p->v.Name.ctx = ctx; + p->lineno = lineno; + return p; +} + +expr_ty +List(asdl_seq * elts, expr_context_ty ctx, int lineno) +{ + expr_ty p; + if (!ctx) { + PyErr_SetString(PyExc_ValueError, + "field ctx is required for List"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = List_kind; + p->v.List.elts = elts; + p->v.List.ctx = ctx; + p->lineno = lineno; + return p; +} + +expr_ty +Tuple(asdl_seq * elts, expr_context_ty ctx, int lineno) +{ + expr_ty p; + if (!ctx) { + PyErr_SetString(PyExc_ValueError, + "field ctx is required for Tuple"); + return NULL; + } + p = (expr_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Tuple_kind; + p->v.Tuple.elts = elts; + p->v.Tuple.ctx = ctx; + p->lineno = lineno; + return p; +} + +slice_ty +Ellipsis() +{ + slice_ty p; + p = (slice_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Ellipsis_kind; + return p; +} + +slice_ty +Slice(expr_ty lower, expr_ty upper, expr_ty step) +{ + slice_ty p; + p = (slice_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Slice_kind; + p->v.Slice.lower = lower; + p->v.Slice.upper = upper; + p->v.Slice.step = step; + return p; +} + +slice_ty +ExtSlice(asdl_seq * dims) +{ + slice_ty p; + p = (slice_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = ExtSlice_kind; + p->v.ExtSlice.dims = dims; + return p; +} + +slice_ty +Index(expr_ty value) +{ + slice_ty p; + if (!value) { + PyErr_SetString(PyExc_ValueError, + "field value is required for Index"); + return NULL; + } + p = (slice_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->kind = Index_kind; + p->v.Index.value = value; + return p; +} + +comprehension_ty +comprehension(expr_ty target, expr_ty iter, asdl_seq * ifs) +{ + comprehension_ty p; + if (!target) { + PyErr_SetString(PyExc_ValueError, + "field target is required for comprehension"); + return NULL; + } + if (!iter) { + PyErr_SetString(PyExc_ValueError, + "field iter is required for comprehension"); + return NULL; + } + p = (comprehension_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->target = target; + p->iter = iter; + p->ifs = ifs; + return p; +} + +excepthandler_ty +excepthandler(expr_ty type, expr_ty name, asdl_seq * body) +{ + excepthandler_ty p; + p = (excepthandler_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->type = type; + p->name = name; + p->body = body; + return p; +} + +arguments_ty +arguments(asdl_seq * args, identifier vararg, identifier kwarg, asdl_seq * + defaults) +{ + arguments_ty p; + p = (arguments_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->args = args; + p->vararg = vararg; + p->kwarg = kwarg; + p->defaults = defaults; + return p; +} + +keyword_ty +keyword(identifier arg, expr_ty value) +{ + keyword_ty p; + if (!arg) { + PyErr_SetString(PyExc_ValueError, + "field arg is required for keyword"); + return NULL; + } + if (!value) { + PyErr_SetString(PyExc_ValueError, + "field value is required for keyword"); + return NULL; + } + p = (keyword_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->arg = arg; + p->value = value; + return p; +} + +alias_ty +alias(identifier name, identifier asname) +{ + alias_ty p; + if (!name) { + PyErr_SetString(PyExc_ValueError, + "field name is required for alias"); + return NULL; + } + p = (alias_ty)malloc(sizeof(*p)); + if (!p) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + p->name = name; + p->asname = asname; + return p; +} + +static void +free_seq_exprs(asdl_seq *seq) +{ + int i, n; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_expr((expr_ty)asdl_seq_GET(seq, i)); + asdl_seq_free(seq); +} + +static void +free_seq_stmts(asdl_seq *seq) +{ + int i, n; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_stmt((stmt_ty)asdl_seq_GET(seq, i)); + asdl_seq_free(seq); +} + +void +free_mod(mod_ty o) +{ + if (!o) + return; + + switch (o->kind) { + case Module_kind: + free_seq_stmts(o->v.Module.body); + break; + case Interactive_kind: + free_seq_stmts(o->v.Interactive.body); + break; + case Expression_kind: + free_expr((expr_ty)o->v.Expression.body); + break; + case Suite_kind: + free_seq_stmts(o->v.Suite.body); + break; + } + + free(o); +} + +void +free_stmt(stmt_ty o) +{ + int i, n; + asdl_seq *seq; + + if (!o) + return; + + switch (o->kind) { + case FunctionDef_kind: + Py_DECREF((identifier)o->v.FunctionDef.name); + free_arguments((arguments_ty)o->v.FunctionDef.args); + free_seq_stmts(o->v.FunctionDef.body); + free_seq_exprs(o->v.FunctionDef.decorators); + break; + case ClassDef_kind: + Py_DECREF((identifier)o->v.ClassDef.name); + free_seq_exprs(o->v.ClassDef.bases); + free_seq_stmts(o->v.ClassDef.body); + break; + case Return_kind: + if (o->v.Return.value) { + free_expr((expr_ty)o->v.Return.value); + } + break; + case Delete_kind: + free_seq_exprs(o->v.Delete.targets); + break; + case Assign_kind: + free_seq_exprs(o->v.Assign.targets); + free_expr((expr_ty)o->v.Assign.value); + break; + case AugAssign_kind: + free_expr((expr_ty)o->v.AugAssign.target); + free_operator((operator_ty)o->v.AugAssign.op); + free_expr((expr_ty)o->v.AugAssign.value); + break; + case Print_kind: + if (o->v.Print.dest) { + free_expr((expr_ty)o->v.Print.dest); + } + free_seq_exprs(o->v.Print.values); + break; + case For_kind: + free_expr((expr_ty)o->v.For.target); + free_expr((expr_ty)o->v.For.iter); + free_seq_stmts(o->v.For.body); + free_seq_stmts(o->v.For.orelse); + break; + case While_kind: + free_expr((expr_ty)o->v.While.test); + free_seq_stmts(o->v.While.body); + free_seq_stmts(o->v.While.orelse); + break; + case If_kind: + free_expr((expr_ty)o->v.If.test); + free_seq_stmts(o->v.If.body); + free_seq_stmts(o->v.If.orelse); + break; + case Raise_kind: + if (o->v.Raise.type) { + free_expr((expr_ty)o->v.Raise.type); + } + if (o->v.Raise.inst) { + free_expr((expr_ty)o->v.Raise.inst); + } + if (o->v.Raise.tback) { + free_expr((expr_ty)o->v.Raise.tback); + } + break; + case TryExcept_kind: + free_seq_stmts(o->v.TryExcept.body); + seq = o->v.TryExcept.handlers; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_excepthandler((excepthandler_ty)asdl_seq_GET(seq, + i)); + asdl_seq_free(seq); + free_seq_stmts(o->v.TryExcept.orelse); + break; + case TryFinally_kind: + free_seq_stmts(o->v.TryFinally.body); + free_seq_stmts(o->v.TryFinally.finalbody); + break; + case Assert_kind: + free_expr((expr_ty)o->v.Assert.test); + if (o->v.Assert.msg) { + free_expr((expr_ty)o->v.Assert.msg); + } + break; + case Import_kind: + seq = o->v.Import.names; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_alias((alias_ty)asdl_seq_GET(seq, i)); + asdl_seq_free(seq); + break; + case ImportFrom_kind: + Py_DECREF((identifier)o->v.ImportFrom.module); + seq = o->v.ImportFrom.names; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_alias((alias_ty)asdl_seq_GET(seq, i)); + asdl_seq_free(seq); + break; + case Exec_kind: + free_expr((expr_ty)o->v.Exec.body); + if (o->v.Exec.globals) { + free_expr((expr_ty)o->v.Exec.globals); + } + if (o->v.Exec.locals) { + free_expr((expr_ty)o->v.Exec.locals); + } + break; + case Global_kind: + seq = o->v.Global.names; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + Py_DECREF((identifier)asdl_seq_GET(seq, i)); + asdl_seq_free(seq); + break; + case Expr_kind: + free_expr((expr_ty)o->v.Expr.value); + break; + case Pass_kind: + break; + case Break_kind: + break; + case Continue_kind: + break; + } + + free(o); +} + +void +free_expr(expr_ty o) +{ + int i, n; + asdl_seq *seq; + + if (!o) + return; + + switch (o->kind) { + case BoolOp_kind: + free_boolop((boolop_ty)o->v.BoolOp.op); + free_seq_exprs(o->v.BoolOp.values); + break; + case BinOp_kind: + free_expr((expr_ty)o->v.BinOp.left); + free_operator((operator_ty)o->v.BinOp.op); + free_expr((expr_ty)o->v.BinOp.right); + break; + case UnaryOp_kind: + free_unaryop((unaryop_ty)o->v.UnaryOp.op); + free_expr((expr_ty)o->v.UnaryOp.operand); + break; + case Lambda_kind: + free_arguments((arguments_ty)o->v.Lambda.args); + free_expr((expr_ty)o->v.Lambda.body); + break; + case Dict_kind: + free_seq_exprs(o->v.Dict.keys); + free_seq_exprs(o->v.Dict.values); + break; + case ListComp_kind: + free_expr((expr_ty)o->v.ListComp.elt); + seq = o->v.ListComp.generators; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_comprehension((comprehension_ty)asdl_seq_GET(seq, + i)); + asdl_seq_free(seq); + break; + case GeneratorExp_kind: + free_expr((expr_ty)o->v.GeneratorExp.elt); + seq = o->v.GeneratorExp.generators; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_comprehension((comprehension_ty)asdl_seq_GET(seq, + i)); + asdl_seq_free(seq); + break; + case Yield_kind: + if (o->v.Yield.value) { + free_expr((expr_ty)o->v.Yield.value); + } + break; + case Compare_kind: + free_expr((expr_ty)o->v.Compare.left); + seq = o->v.Compare.ops; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_cmpop((cmpop_ty)asdl_seq_GET(seq, i)); + asdl_seq_free(seq); + free_seq_exprs(o->v.Compare.comparators); + break; + case Call_kind: + free_expr((expr_ty)o->v.Call.func); + free_seq_exprs(o->v.Call.args); + seq = o->v.Call.keywords; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_keyword((keyword_ty)asdl_seq_GET(seq, i)); + asdl_seq_free(seq); + if (o->v.Call.starargs) { + free_expr((expr_ty)o->v.Call.starargs); + } + if (o->v.Call.kwargs) { + free_expr((expr_ty)o->v.Call.kwargs); + } + break; + case Repr_kind: + free_expr((expr_ty)o->v.Repr.value); + break; + case Num_kind: + Py_DECREF((object)o->v.Num.n); + break; + case Str_kind: + Py_DECREF((string)o->v.Str.s); + break; + case Attribute_kind: + free_expr((expr_ty)o->v.Attribute.value); + Py_DECREF((identifier)o->v.Attribute.attr); + free_expr_context((expr_context_ty)o->v.Attribute.ctx); + break; + case Subscript_kind: + free_expr((expr_ty)o->v.Subscript.value); + free_slice((slice_ty)o->v.Subscript.slice); + free_expr_context((expr_context_ty)o->v.Subscript.ctx); + break; + case Name_kind: + Py_DECREF((identifier)o->v.Name.id); + free_expr_context((expr_context_ty)o->v.Name.ctx); + break; + case List_kind: + free_seq_exprs(o->v.List.elts); + free_expr_context((expr_context_ty)o->v.List.ctx); + break; + case Tuple_kind: + free_seq_exprs(o->v.Tuple.elts); + free_expr_context((expr_context_ty)o->v.Tuple.ctx); + break; + } + + free(o); +} + +void +free_expr_context(expr_context_ty o) +{ + if (!o) + return; + +} + +void +free_slice(slice_ty o) +{ + int i, n; + asdl_seq *seq; + + if (!o) + return; + + switch (o->kind) { + case Ellipsis_kind: + break; + case Slice_kind: + if (o->v.Slice.lower) { + free_expr((expr_ty)o->v.Slice.lower); + } + if (o->v.Slice.upper) { + free_expr((expr_ty)o->v.Slice.upper); + } + if (o->v.Slice.step) { + free_expr((expr_ty)o->v.Slice.step); + } + break; + case ExtSlice_kind: + seq = o->v.ExtSlice.dims; + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_slice((slice_ty)asdl_seq_GET(seq, i)); + asdl_seq_free(seq); + break; + case Index_kind: + free_expr((expr_ty)o->v.Index.value); + break; + } + + free(o); +} + +void +free_boolop(boolop_ty o) +{ + if (!o) + return; + +} + +void +free_operator(operator_ty o) +{ + if (!o) + return; + +} + +void +free_unaryop(unaryop_ty o) +{ + if (!o) + return; + +} + +void +free_cmpop(cmpop_ty o) +{ + if (!o) + return; + +} + +void +free_comprehension(comprehension_ty o) +{ + if (!o) + return; + + free_expr((expr_ty)o->target); + free_expr((expr_ty)o->iter); + free_seq_exprs(o->ifs); + + free(o); +} + +void +free_excepthandler(excepthandler_ty o) +{ + if (!o) + return; + + if (o->type) { + free_expr((expr_ty)o->type); + } + if (o->name) { + free_expr((expr_ty)o->name); + } + free_seq_stmts(o->body); + + free(o); +} + +void +free_arguments(arguments_ty o) +{ + if (!o) + return; + + free_seq_exprs(o->args); + if (o->vararg) { + Py_DECREF((identifier)o->vararg); + } + if (o->kwarg) { + Py_DECREF((identifier)o->kwarg); + } + free_seq_exprs(o->defaults); + + free(o); +} + +void +free_keyword(keyword_ty o) +{ + if (!o) + return; + + Py_DECREF((identifier)o->arg); + free_expr((expr_ty)o->value); + + free(o); +} + +void +free_alias(alias_ty o) +{ + if (!o) + return; + + Py_DECREF((identifier)o->name); + if (o->asname) { + Py_DECREF((identifier)o->asname); + } + + free(o); +} + +int +marshal_write_mod(PyObject **buf, int *off, mod_ty o) +{ + int i; + switch (o->kind) { + case Module_kind: + marshal_write_int(buf, off, 1); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Module.body)); + for (i = 0; i < asdl_seq_LEN(o->v.Module.body); i++) { + void *elt = asdl_seq_GET(o->v.Module.body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + break; + case Interactive_kind: + marshal_write_int(buf, off, 2); + marshal_write_int(buf, off, + asdl_seq_LEN(o->v.Interactive.body)); + for (i = 0; i < asdl_seq_LEN(o->v.Interactive.body); i++) { + void *elt = asdl_seq_GET(o->v.Interactive.body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + break; + case Expression_kind: + marshal_write_int(buf, off, 3); + marshal_write_expr(buf, off, o->v.Expression.body); + break; + case Suite_kind: + marshal_write_int(buf, off, 4); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Suite.body)); + for (i = 0; i < asdl_seq_LEN(o->v.Suite.body); i++) { + void *elt = asdl_seq_GET(o->v.Suite.body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + break; + } + return 1; +} + +int +marshal_write_stmt(PyObject **buf, int *off, stmt_ty o) +{ + int i; + switch (o->kind) { + case FunctionDef_kind: + marshal_write_int(buf, off, 1); + marshal_write_identifier(buf, off, o->v.FunctionDef.name); + marshal_write_arguments(buf, off, o->v.FunctionDef.args); + marshal_write_int(buf, off, + asdl_seq_LEN(o->v.FunctionDef.body)); + for (i = 0; i < asdl_seq_LEN(o->v.FunctionDef.body); i++) { + void *elt = asdl_seq_GET(o->v.FunctionDef.body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + marshal_write_int(buf, off, + asdl_seq_LEN(o->v.FunctionDef.decorators)); + for (i = 0; i < asdl_seq_LEN(o->v.FunctionDef.decorators); i++) + { + void *elt = asdl_seq_GET(o->v.FunctionDef.decorators, + i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + break; + case ClassDef_kind: + marshal_write_int(buf, off, 2); + marshal_write_identifier(buf, off, o->v.ClassDef.name); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.ClassDef.bases)); + for (i = 0; i < asdl_seq_LEN(o->v.ClassDef.bases); i++) { + void *elt = asdl_seq_GET(o->v.ClassDef.bases, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + marshal_write_int(buf, off, asdl_seq_LEN(o->v.ClassDef.body)); + for (i = 0; i < asdl_seq_LEN(o->v.ClassDef.body); i++) { + void *elt = asdl_seq_GET(o->v.ClassDef.body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + break; + case Return_kind: + marshal_write_int(buf, off, 3); + if (o->v.Return.value) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Return.value); + } + else { + marshal_write_int(buf, off, 0); + } + break; + case Delete_kind: + marshal_write_int(buf, off, 4); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Delete.targets)); + for (i = 0; i < asdl_seq_LEN(o->v.Delete.targets); i++) { + void *elt = asdl_seq_GET(o->v.Delete.targets, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + break; + case Assign_kind: + marshal_write_int(buf, off, 5); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Assign.targets)); + for (i = 0; i < asdl_seq_LEN(o->v.Assign.targets); i++) { + void *elt = asdl_seq_GET(o->v.Assign.targets, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + marshal_write_expr(buf, off, o->v.Assign.value); + break; + case AugAssign_kind: + marshal_write_int(buf, off, 6); + marshal_write_expr(buf, off, o->v.AugAssign.target); + marshal_write_operator(buf, off, o->v.AugAssign.op); + marshal_write_expr(buf, off, o->v.AugAssign.value); + break; + case Print_kind: + marshal_write_int(buf, off, 7); + if (o->v.Print.dest) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Print.dest); + } + else { + marshal_write_int(buf, off, 0); + } + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Print.values)); + for (i = 0; i < asdl_seq_LEN(o->v.Print.values); i++) { + void *elt = asdl_seq_GET(o->v.Print.values, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + marshal_write_bool(buf, off, o->v.Print.nl); + break; + case For_kind: + marshal_write_int(buf, off, 8); + marshal_write_expr(buf, off, o->v.For.target); + marshal_write_expr(buf, off, o->v.For.iter); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.For.body)); + for (i = 0; i < asdl_seq_LEN(o->v.For.body); i++) { + void *elt = asdl_seq_GET(o->v.For.body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + marshal_write_int(buf, off, asdl_seq_LEN(o->v.For.orelse)); + for (i = 0; i < asdl_seq_LEN(o->v.For.orelse); i++) { + void *elt = asdl_seq_GET(o->v.For.orelse, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + break; + case While_kind: + marshal_write_int(buf, off, 9); + marshal_write_expr(buf, off, o->v.While.test); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.While.body)); + for (i = 0; i < asdl_seq_LEN(o->v.While.body); i++) { + void *elt = asdl_seq_GET(o->v.While.body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + marshal_write_int(buf, off, asdl_seq_LEN(o->v.While.orelse)); + for (i = 0; i < asdl_seq_LEN(o->v.While.orelse); i++) { + void *elt = asdl_seq_GET(o->v.While.orelse, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + break; + case If_kind: + marshal_write_int(buf, off, 10); + marshal_write_expr(buf, off, o->v.If.test); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.If.body)); + for (i = 0; i < asdl_seq_LEN(o->v.If.body); i++) { + void *elt = asdl_seq_GET(o->v.If.body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + marshal_write_int(buf, off, asdl_seq_LEN(o->v.If.orelse)); + for (i = 0; i < asdl_seq_LEN(o->v.If.orelse); i++) { + void *elt = asdl_seq_GET(o->v.If.orelse, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + break; + case Raise_kind: + marshal_write_int(buf, off, 11); + if (o->v.Raise.type) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Raise.type); + } + else { + marshal_write_int(buf, off, 0); + } + if (o->v.Raise.inst) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Raise.inst); + } + else { + marshal_write_int(buf, off, 0); + } + if (o->v.Raise.tback) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Raise.tback); + } + else { + marshal_write_int(buf, off, 0); + } + break; + case TryExcept_kind: + marshal_write_int(buf, off, 12); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.TryExcept.body)); + for (i = 0; i < asdl_seq_LEN(o->v.TryExcept.body); i++) { + void *elt = asdl_seq_GET(o->v.TryExcept.body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + marshal_write_int(buf, off, + asdl_seq_LEN(o->v.TryExcept.handlers)); + for (i = 0; i < asdl_seq_LEN(o->v.TryExcept.handlers); i++) { + void *elt = asdl_seq_GET(o->v.TryExcept.handlers, i); + marshal_write_excepthandler(buf, off, + (excepthandler_ty)elt); + } + marshal_write_int(buf, off, + asdl_seq_LEN(o->v.TryExcept.orelse)); + for (i = 0; i < asdl_seq_LEN(o->v.TryExcept.orelse); i++) { + void *elt = asdl_seq_GET(o->v.TryExcept.orelse, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + break; + case TryFinally_kind: + marshal_write_int(buf, off, 13); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.TryFinally.body)); + for (i = 0; i < asdl_seq_LEN(o->v.TryFinally.body); i++) { + void *elt = asdl_seq_GET(o->v.TryFinally.body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + marshal_write_int(buf, off, + asdl_seq_LEN(o->v.TryFinally.finalbody)); + for (i = 0; i < asdl_seq_LEN(o->v.TryFinally.finalbody); i++) { + void *elt = asdl_seq_GET(o->v.TryFinally.finalbody, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + break; + case Assert_kind: + marshal_write_int(buf, off, 14); + marshal_write_expr(buf, off, o->v.Assert.test); + if (o->v.Assert.msg) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Assert.msg); + } + else { + marshal_write_int(buf, off, 0); + } + break; + case Import_kind: + marshal_write_int(buf, off, 15); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Import.names)); + for (i = 0; i < asdl_seq_LEN(o->v.Import.names); i++) { + void *elt = asdl_seq_GET(o->v.Import.names, i); + marshal_write_alias(buf, off, (alias_ty)elt); + } + break; + case ImportFrom_kind: + marshal_write_int(buf, off, 16); + marshal_write_identifier(buf, off, o->v.ImportFrom.module); + marshal_write_int(buf, off, + asdl_seq_LEN(o->v.ImportFrom.names)); + for (i = 0; i < asdl_seq_LEN(o->v.ImportFrom.names); i++) { + void *elt = asdl_seq_GET(o->v.ImportFrom.names, i); + marshal_write_alias(buf, off, (alias_ty)elt); + } + break; + case Exec_kind: + marshal_write_int(buf, off, 17); + marshal_write_expr(buf, off, o->v.Exec.body); + if (o->v.Exec.globals) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Exec.globals); + } + else { + marshal_write_int(buf, off, 0); + } + if (o->v.Exec.locals) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Exec.locals); + } + else { + marshal_write_int(buf, off, 0); + } + break; + case Global_kind: + marshal_write_int(buf, off, 18); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Global.names)); + for (i = 0; i < asdl_seq_LEN(o->v.Global.names); i++) { + void *elt = asdl_seq_GET(o->v.Global.names, i); + marshal_write_identifier(buf, off, (identifier)elt); + } + break; + case Expr_kind: + marshal_write_int(buf, off, 19); + marshal_write_expr(buf, off, o->v.Expr.value); + break; + case Pass_kind: + marshal_write_int(buf, off, 20); + break; + case Break_kind: + marshal_write_int(buf, off, 21); + break; + case Continue_kind: + marshal_write_int(buf, off, 22); + break; + } + return 1; +} + +int +marshal_write_expr(PyObject **buf, int *off, expr_ty o) +{ + int i; + switch (o->kind) { + case BoolOp_kind: + marshal_write_int(buf, off, 1); + marshal_write_boolop(buf, off, o->v.BoolOp.op); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.BoolOp.values)); + for (i = 0; i < asdl_seq_LEN(o->v.BoolOp.values); i++) { + void *elt = asdl_seq_GET(o->v.BoolOp.values, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + break; + case BinOp_kind: + marshal_write_int(buf, off, 2); + marshal_write_expr(buf, off, o->v.BinOp.left); + marshal_write_operator(buf, off, o->v.BinOp.op); + marshal_write_expr(buf, off, o->v.BinOp.right); + break; + case UnaryOp_kind: + marshal_write_int(buf, off, 3); + marshal_write_unaryop(buf, off, o->v.UnaryOp.op); + marshal_write_expr(buf, off, o->v.UnaryOp.operand); + break; + case Lambda_kind: + marshal_write_int(buf, off, 4); + marshal_write_arguments(buf, off, o->v.Lambda.args); + marshal_write_expr(buf, off, o->v.Lambda.body); + break; + case Dict_kind: + marshal_write_int(buf, off, 5); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Dict.keys)); + for (i = 0; i < asdl_seq_LEN(o->v.Dict.keys); i++) { + void *elt = asdl_seq_GET(o->v.Dict.keys, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Dict.values)); + for (i = 0; i < asdl_seq_LEN(o->v.Dict.values); i++) { + void *elt = asdl_seq_GET(o->v.Dict.values, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + break; + case ListComp_kind: + marshal_write_int(buf, off, 6); + marshal_write_expr(buf, off, o->v.ListComp.elt); + marshal_write_int(buf, off, + asdl_seq_LEN(o->v.ListComp.generators)); + for (i = 0; i < asdl_seq_LEN(o->v.ListComp.generators); i++) { + void *elt = asdl_seq_GET(o->v.ListComp.generators, i); + marshal_write_comprehension(buf, off, + (comprehension_ty)elt); + } + break; + case GeneratorExp_kind: + marshal_write_int(buf, off, 7); + marshal_write_expr(buf, off, o->v.GeneratorExp.elt); + marshal_write_int(buf, off, + asdl_seq_LEN(o->v.GeneratorExp.generators)); + for (i = 0; i < asdl_seq_LEN(o->v.GeneratorExp.generators); + i++) { + void *elt = asdl_seq_GET(o->v.GeneratorExp.generators, + i); + marshal_write_comprehension(buf, off, + (comprehension_ty)elt); + } + break; + case Yield_kind: + marshal_write_int(buf, off, 8); + if (o->v.Yield.value) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Yield.value); + } + else { + marshal_write_int(buf, off, 0); + } + break; + case Compare_kind: + marshal_write_int(buf, off, 9); + marshal_write_expr(buf, off, o->v.Compare.left); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Compare.ops)); + for (i = 0; i < asdl_seq_LEN(o->v.Compare.ops); i++) { + void *elt = asdl_seq_GET(o->v.Compare.ops, i); + marshal_write_cmpop(buf, off, (cmpop_ty)elt); + } + marshal_write_int(buf, off, + asdl_seq_LEN(o->v.Compare.comparators)); + for (i = 0; i < asdl_seq_LEN(o->v.Compare.comparators); i++) { + void *elt = asdl_seq_GET(o->v.Compare.comparators, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + break; + case Call_kind: + marshal_write_int(buf, off, 10); + marshal_write_expr(buf, off, o->v.Call.func); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Call.args)); + for (i = 0; i < asdl_seq_LEN(o->v.Call.args); i++) { + void *elt = asdl_seq_GET(o->v.Call.args, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Call.keywords)); + for (i = 0; i < asdl_seq_LEN(o->v.Call.keywords); i++) { + void *elt = asdl_seq_GET(o->v.Call.keywords, i); + marshal_write_keyword(buf, off, (keyword_ty)elt); + } + if (o->v.Call.starargs) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Call.starargs); + } + else { + marshal_write_int(buf, off, 0); + } + if (o->v.Call.kwargs) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Call.kwargs); + } + else { + marshal_write_int(buf, off, 0); + } + break; + case Repr_kind: + marshal_write_int(buf, off, 11); + marshal_write_expr(buf, off, o->v.Repr.value); + break; + case Num_kind: + marshal_write_int(buf, off, 12); + marshal_write_object(buf, off, o->v.Num.n); + break; + case Str_kind: + marshal_write_int(buf, off, 13); + marshal_write_string(buf, off, o->v.Str.s); + break; + case Attribute_kind: + marshal_write_int(buf, off, 14); + marshal_write_expr(buf, off, o->v.Attribute.value); + marshal_write_identifier(buf, off, o->v.Attribute.attr); + marshal_write_expr_context(buf, off, o->v.Attribute.ctx); + break; + case Subscript_kind: + marshal_write_int(buf, off, 15); + marshal_write_expr(buf, off, o->v.Subscript.value); + marshal_write_slice(buf, off, o->v.Subscript.slice); + marshal_write_expr_context(buf, off, o->v.Subscript.ctx); + break; + case Name_kind: + marshal_write_int(buf, off, 16); + marshal_write_identifier(buf, off, o->v.Name.id); + marshal_write_expr_context(buf, off, o->v.Name.ctx); + break; + case List_kind: + marshal_write_int(buf, off, 17); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.List.elts)); + for (i = 0; i < asdl_seq_LEN(o->v.List.elts); i++) { + void *elt = asdl_seq_GET(o->v.List.elts, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + marshal_write_expr_context(buf, off, o->v.List.ctx); + break; + case Tuple_kind: + marshal_write_int(buf, off, 18); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.Tuple.elts)); + for (i = 0; i < asdl_seq_LEN(o->v.Tuple.elts); i++) { + void *elt = asdl_seq_GET(o->v.Tuple.elts, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + marshal_write_expr_context(buf, off, o->v.Tuple.ctx); + break; + } + return 1; +} + +int +marshal_write_expr_context(PyObject **buf, int *off, expr_context_ty o) +{ + int i; + switch (o) { + case Load: + marshal_write_int(buf, off, 1); + break; + case Store: + marshal_write_int(buf, off, 2); + break; + case Del: + marshal_write_int(buf, off, 3); + break; + case AugLoad: + marshal_write_int(buf, off, 4); + break; + case AugStore: + marshal_write_int(buf, off, 5); + break; + case Param: + marshal_write_int(buf, off, 6); + break; + } + return 1; +} + +int +marshal_write_slice(PyObject **buf, int *off, slice_ty o) +{ + int i; + switch (o->kind) { + case Ellipsis_kind: + marshal_write_int(buf, off, 1); + break; + case Slice_kind: + marshal_write_int(buf, off, 2); + if (o->v.Slice.lower) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Slice.lower); + } + else { + marshal_write_int(buf, off, 0); + } + if (o->v.Slice.upper) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Slice.upper); + } + else { + marshal_write_int(buf, off, 0); + } + if (o->v.Slice.step) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->v.Slice.step); + } + else { + marshal_write_int(buf, off, 0); + } + break; + case ExtSlice_kind: + marshal_write_int(buf, off, 3); + marshal_write_int(buf, off, asdl_seq_LEN(o->v.ExtSlice.dims)); + for (i = 0; i < asdl_seq_LEN(o->v.ExtSlice.dims); i++) { + void *elt = asdl_seq_GET(o->v.ExtSlice.dims, i); + marshal_write_slice(buf, off, (slice_ty)elt); + } + break; + case Index_kind: + marshal_write_int(buf, off, 4); + marshal_write_expr(buf, off, o->v.Index.value); + break; + } + return 1; +} + +int +marshal_write_boolop(PyObject **buf, int *off, boolop_ty o) +{ + int i; + switch (o) { + case And: + marshal_write_int(buf, off, 1); + break; + case Or: + marshal_write_int(buf, off, 2); + break; + } + return 1; +} + +int +marshal_write_operator(PyObject **buf, int *off, operator_ty o) +{ + int i; + switch (o) { + case Add: + marshal_write_int(buf, off, 1); + break; + case Sub: + marshal_write_int(buf, off, 2); + break; + case Mult: + marshal_write_int(buf, off, 3); + break; + case Div: + marshal_write_int(buf, off, 4); + break; + case Mod: + marshal_write_int(buf, off, 5); + break; + case Pow: + marshal_write_int(buf, off, 6); + break; + case LShift: + marshal_write_int(buf, off, 7); + break; + case RShift: + marshal_write_int(buf, off, 8); + break; + case BitOr: + marshal_write_int(buf, off, 9); + break; + case BitXor: + marshal_write_int(buf, off, 10); + break; + case BitAnd: + marshal_write_int(buf, off, 11); + break; + case FloorDiv: + marshal_write_int(buf, off, 12); + break; + } + return 1; +} + +int +marshal_write_unaryop(PyObject **buf, int *off, unaryop_ty o) +{ + int i; + switch (o) { + case Invert: + marshal_write_int(buf, off, 1); + break; + case Not: + marshal_write_int(buf, off, 2); + break; + case UAdd: + marshal_write_int(buf, off, 3); + break; + case USub: + marshal_write_int(buf, off, 4); + break; + } + return 1; +} + +int +marshal_write_cmpop(PyObject **buf, int *off, cmpop_ty o) +{ + int i; + switch (o) { + case Eq: + marshal_write_int(buf, off, 1); + break; + case NotEq: + marshal_write_int(buf, off, 2); + break; + case Lt: + marshal_write_int(buf, off, 3); + break; + case LtE: + marshal_write_int(buf, off, 4); + break; + case Gt: + marshal_write_int(buf, off, 5); + break; + case GtE: + marshal_write_int(buf, off, 6); + break; + case Is: + marshal_write_int(buf, off, 7); + break; + case IsNot: + marshal_write_int(buf, off, 8); + break; + case In: + marshal_write_int(buf, off, 9); + break; + case NotIn: + marshal_write_int(buf, off, 10); + break; + } + return 1; +} + +int +marshal_write_comprehension(PyObject **buf, int *off, comprehension_ty o) +{ + int i; + marshal_write_expr(buf, off, o->target); + marshal_write_expr(buf, off, o->iter); + marshal_write_int(buf, off, asdl_seq_LEN(o->ifs)); + for (i = 0; i < asdl_seq_LEN(o->ifs); i++) { + void *elt = asdl_seq_GET(o->ifs, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + return 1; +} + +int +marshal_write_excepthandler(PyObject **buf, int *off, excepthandler_ty o) +{ + int i; + if (o->type) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->type); + } + else { + marshal_write_int(buf, off, 0); + } + if (o->name) { + marshal_write_int(buf, off, 1); + marshal_write_expr(buf, off, o->name); + } + else { + marshal_write_int(buf, off, 0); + } + marshal_write_int(buf, off, asdl_seq_LEN(o->body)); + for (i = 0; i < asdl_seq_LEN(o->body); i++) { + void *elt = asdl_seq_GET(o->body, i); + marshal_write_stmt(buf, off, (stmt_ty)elt); + } + return 1; +} + +int +marshal_write_arguments(PyObject **buf, int *off, arguments_ty o) +{ + int i; + marshal_write_int(buf, off, asdl_seq_LEN(o->args)); + for (i = 0; i < asdl_seq_LEN(o->args); i++) { + void *elt = asdl_seq_GET(o->args, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + if (o->vararg) { + marshal_write_int(buf, off, 1); + marshal_write_identifier(buf, off, o->vararg); + } + else { + marshal_write_int(buf, off, 0); + } + if (o->kwarg) { + marshal_write_int(buf, off, 1); + marshal_write_identifier(buf, off, o->kwarg); + } + else { + marshal_write_int(buf, off, 0); + } + marshal_write_int(buf, off, asdl_seq_LEN(o->defaults)); + for (i = 0; i < asdl_seq_LEN(o->defaults); i++) { + void *elt = asdl_seq_GET(o->defaults, i); + marshal_write_expr(buf, off, (expr_ty)elt); + } + return 1; +} + +int +marshal_write_keyword(PyObject **buf, int *off, keyword_ty o) +{ + int i; + marshal_write_identifier(buf, off, o->arg); + marshal_write_expr(buf, off, o->value); + return 1; +} + +int +marshal_write_alias(PyObject **buf, int *off, alias_ty o) +{ + int i; + marshal_write_identifier(buf, off, o->name); + if (o->asname) { + marshal_write_int(buf, off, 1); + marshal_write_identifier(buf, off, o->asname); + } + else { + marshal_write_int(buf, off, 0); + } + return 1; +} + diff --git a/Python/asdl.c b/Python/asdl.c new file mode 100644 index 0000000..bb29857 --- /dev/null +++ b/Python/asdl.c @@ -0,0 +1,92 @@ +#include "Python.h" +#include "asdl.h" + +asdl_seq * +asdl_seq_new(int size) +{ + asdl_seq *seq = NULL; + size_t n = sizeof(asdl_seq) + + (size ? (sizeof(void *) * (size - 1)) : 0); + + seq = (asdl_seq *)PyObject_Malloc(n); + if (!seq) { + PyErr_SetString(PyExc_MemoryError, "no memory"); + return NULL; + } + memset(seq, 0, n); + seq->size = size; + return seq; +} + +void +asdl_seq_free(asdl_seq *seq) +{ + PyObject_Free(seq); +} + +#define CHECKSIZE(BUF, OFF, MIN) { \ + int need = *(OFF) + MIN; \ + if (need >= PyString_GET_SIZE(*(BUF))) { \ + int newsize = PyString_GET_SIZE(*(BUF)) * 2; \ + if (newsize < need) \ + newsize = need; \ + if (_PyString_Resize((BUF), newsize) < 0) \ + return 0; \ + } \ +} + +int +marshal_write_int(PyObject **buf, int *offset, int x) +{ + char *s; + + CHECKSIZE(buf, offset, 4) + s = PyString_AS_STRING(*buf) + (*offset); + s[0] = (x & 0xff); + s[1] = (x >> 8) & 0xff; + s[2] = (x >> 16) & 0xff; + s[3] = (x >> 24) & 0xff; + *offset += 4; + return 1; +} + +int +marshal_write_bool(PyObject **buf, int *offset, bool b) +{ + if (b) + marshal_write_int(buf, offset, 1); + else + marshal_write_int(buf, offset, 0); + return 1; +} + +int +marshal_write_identifier(PyObject **buf, int *offset, identifier id) +{ + int l = PyString_GET_SIZE(id); + marshal_write_int(buf, offset, l); + CHECKSIZE(buf, offset, l); + memcpy(PyString_AS_STRING(*buf) + *offset, + PyString_AS_STRING(id), l); + *offset += l; + return 1; +} + +int +marshal_write_string(PyObject **buf, int *offset, string s) +{ + int len = PyString_GET_SIZE(s); + marshal_write_int(buf, offset, len); + CHECKSIZE(buf, offset, len); + memcpy(PyString_AS_STRING(*buf) + *offset, + PyString_AS_STRING(s), len); + *offset += len; + return 1; +} + +int +marshal_write_object(PyObject **buf, int *offset, object s) +{ + /* XXX */ + return 0; +} diff --git a/Python/ast.c b/Python/ast.c new file mode 100644 index 0000000..475382c --- /dev/null +++ b/Python/ast.c @@ -0,0 +1,3114 @@ +/* + * 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 "ast.h" +#include "token.h" +#include "parsetok.h" +#include "graminit.h" + +#include + +#if 0 +#define fprintf if (0) fprintf +#endif + +/* XXX TO DO + - re-indent this file (should be done) + - internal error checking (freeing memory, etc.) + - syntax errors +*/ + + +/* Data structure used internally */ +struct compiling { + char *c_encoding; /* source encoding */ +}; + +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 *, int); +static expr_ty ast_for_testlist(struct compiling *, const node *, int); + +/* Note different signature for ast_for_call */ +static expr_ty ast_for_call(struct compiling *, const node *, expr_ty); + +static PyObject *parsenumber(const char *); +static PyObject *parsestr(const char *s, const char *encoding); +static PyObject *parsestrplus(struct compiling *, const node *n); + +extern grammar _PyParser_Grammar; /* From graminit.c */ + +#ifndef LINENO +#define LINENO(n) ((n)->n_lineno) +#endif + +#define NEW_IDENTIFIER(n) PyString_InternFromString(STR(n)) + +static void +asdl_stmt_seq_free(asdl_seq* seq) +{ + int n, i; + + if (!seq) + return; + + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_stmt(asdl_seq_GET(seq, i)); + asdl_seq_free(seq); +} + +static void +asdl_expr_seq_free(asdl_seq* seq) +{ + int n, i; + + if (!seq) + return; + + n = asdl_seq_LEN(seq); + for (i = 0; i < n; i++) + free_expr(asdl_seq_GET(seq, i)); + asdl_seq_free(seq); +} + +/* 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; + int 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) + return; + Py_DECREF(value); + + loc = PyErr_ProgramText(filename, lineno); + if (!loc) { + Py_INCREF(Py_None); + loc = Py_None; + } + tmp = Py_BuildValue("(ziOO)", filename, lineno, Py_None, loc); + Py_DECREF(loc); + if (!tmp) + return; + value = Py_BuildValue("(OO)", errstr, tmp); + Py_DECREF(errstr); + Py_DECREF(tmp); + if (!value) + return; + PyErr_Restore(type, value, tback); +} + +/* 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\n", + 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) +{ + int i, j, 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"; + } else if (TYPE(n) == encoding_decl) { + c.c_encoding = STR(n); + n = CHILD(n, 0); + } else { + c.c_encoding = NULL; + } + + switch (TYPE(n)) { + case file_input: + stmts = asdl_seq_new(num_stmts(n)); + 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_APPEND(stmts, 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_APPEND(stmts, s); + } + } + } + return Module(stmts); + case eval_input: { + expr_ty testlist_ast; + + /* XXX Why not gen_for here? */ + testlist_ast = ast_for_testlist(&c, CHILD(n, 0), 0); + if (!testlist_ast) + goto error; + return Expression(testlist_ast); + } + case single_input: + if (TYPE(CHILD(n, 0)) == NEWLINE) { + stmts = asdl_seq_new(1); + if (!stmts) + goto error; + asdl_seq_SET(stmts, 0, Pass(n->n_lineno)); + return Interactive(stmts); + } + else { + n = CHILD(n, 0); + num = num_stmts(n); + stmts = asdl_seq_new(num); + if (!stmts) + goto error; + if (num == 1) { + stmt_ty 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) { + stmt_ty s; + 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); + } + default: + goto error; + } + error: + if (stmts) + asdl_stmt_seq_free(stmts); + 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 0; + } +} + +/* Set the context ctx for expr_ty e returning 0 on success, -1 on error. + + 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. + + If e is a sequential type, items in sequence will also have their context + set. + +*/ + +static int +set_context(expr_ty e, expr_context_ty ctx, const node *n) +{ + asdl_seq *s = NULL; + + switch (e->kind) { + case Attribute_kind: + if (ctx == Store && + !strcmp(PyString_AS_STRING(e->v.Attribute.attr), "None")) { + return ast_error(n, "assignment to None"); + } + e->v.Attribute.ctx = ctx; + break; + case Subscript_kind: + e->v.Subscript.ctx = ctx; + break; + case Name_kind: + if (ctx == Store && + !strcmp(PyString_AS_STRING(e->v.Name.id), "None")) { + return ast_error(n, "assignment to None"); + } + 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) == 0) + return ast_error(n, "can't assign to ()"); + e->v.Tuple.ctx = ctx; + s = e->v.Tuple.elts; + break; + case Call_kind: + if (ctx == Store) + return ast_error(n, "can't assign to function call"); + else if (ctx == Del) + return ast_error(n, "can't delete function call"); + else + return ast_error(n, "unexpected operation on function call"); + break; + case BinOp_kind: + return ast_error(n, "can't assign to operator"); + case GeneratorExp_kind: + return ast_error(n, "assignment to generator expression " + "not possible"); + case Num_kind: + case Str_kind: + return ast_error(n, "can't assign to literal"); + default: { + char buf[300]; + PyOS_snprintf(buf, sizeof(buf), + "unexpected expression in assignment %d (line %d)", + e->kind, e->lineno); + return ast_error(n, buf); + } + } + /* If the LHS is a list or tuple, we need to set the assignment + context for all the tuple elements. + */ + if (s) { + int i; + + for (i = 0; i < asdl_seq_LEN(s); i++) { + if (!set_context(asdl_seq_GET(s, i), ctx, n)) + return 0; + } + } + return 1; +} + +static operator_ty +ast_for_augassign(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_Exception, "invalid augassign: %s", STR(n)); + return 0; + } +} + +static cmpop_ty +ast_for_comp_op(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: /* == */ + case EQUAL: + 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_Exception, "invalid comp_op: %s", + STR(n)); + return 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_Exception, "invalid comp_op: %s %s", + STR(CHILD(n, 0)), STR(CHILD(n, 1))); + return 0; + } + } + PyErr_Format(PyExc_Exception, "invalid comp_op: has %d children", + NCH(n)); + return 0; +} + +static asdl_seq * +seq_for_testlist(struct compiling *c, const node *n) +{ + /* testlist: test (',' test)* [','] */ + assert(TYPE(n) == testlist + || TYPE(n) == listmaker + || TYPE(n) == testlist_gexp + || TYPE(n) == testlist_safe + ); + asdl_seq *seq; + expr_ty expression; + int i; + + seq = asdl_seq_new((NCH(n) + 1) / 2); + if (!seq) + return NULL; + + for (i = 0; i < NCH(n); i += 2) { + REQ(CHILD(n, i), test); + + expression = ast_for_expr(c, CHILD(n, i)); + if (!expression) { + asdl_seq_free(seq); + return NULL; + } + + assert(i / 2 < seq->size); + asdl_seq_SET(seq, i / 2, expression); + } + return seq; +} + +static expr_ty +compiler_complex_args(const node *n) +{ + int i, len = (NCH(n) + 1) / 2; + expr_ty result; + asdl_seq *args = asdl_seq_new(len); + if (!args) + return NULL; + + REQ(n, fplist); + + for (i = 0; i < len; i++) { + const node *child = CHILD(CHILD(n, 2*i), 0); + expr_ty arg; + if (TYPE(child) == NAME) { + if (!strcmp(STR(child), "None")) { + ast_error(child, "assignment to None"); + return NULL; + } + arg = Name(NEW_IDENTIFIER(child), Store, LINENO(child)); + } + else + arg = compiler_complex_args(CHILD(CHILD(n, 2*i), 1)); + set_context(arg, Store, n); + asdl_seq_SET(args, i, arg); + } + + result = Tuple(args, Store, LINENO(n)); + set_context(result, Store, n); + return result; +} + +/* Create AST for argument list. + + XXX TO DO: + - check for invalid argument lists like normal after default +*/ + +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, 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); + 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) : NULL); + if (!args && n_args) + return NULL; /* Don't need to go to NULL; nothing allocated */ + defaults = (n_defaults ? asdl_seq_new(n_defaults) : NULL); + if (!defaults && n_defaults) + goto error; + + /* fpdef: NAME | '(' fplist ')' + fplist: fpdef (',' fpdef)* [','] + */ + i = 0; + while (i < NCH(n)) { + ch = CHILD(n, i); + switch (TYPE(ch)) { + case 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) { + asdl_seq_APPEND(defaults, + ast_for_expr(c, CHILD(n, i + 2))); + i += 2; + found_default = 1; + } + else if (found_default) { + ast_error(n, + "non-default argument follows default argument"); + goto error; + } + + if (NCH(ch) == 3) { + asdl_seq_APPEND(args, + compiler_complex_args(CHILD(ch, 1))); + } + else if (TYPE(CHILD(ch, 0)) == NAME) { + if (!strcmp(STR(CHILD(ch, 0)), "None")) { + ast_error(CHILD(ch, 0), "assignment to None"); + goto error; + } + expr_ty name = Name(NEW_IDENTIFIER(CHILD(ch, 0)), + Param, LINENO(ch)); + if (!name) + goto error; + asdl_seq_APPEND(args, name); + + } + i += 2; /* the name and the comma */ + break; + case STAR: + if (!strcmp(STR(CHILD(n, i+1)), "None")) { + ast_error(CHILD(n, i+1), "assignment to None"); + goto error; + } + vararg = NEW_IDENTIFIER(CHILD(n, i+1)); + i += 3; + break; + case DOUBLESTAR: + if (!strcmp(STR(CHILD(n, i+1)), "None")) { + ast_error(CHILD(n, i+1), "assignment to None"); + goto error; + } + kwarg = NEW_IDENTIFIER(CHILD(n, i+1)); + i += 3; + break; + default: + PyErr_Format(PyExc_Exception, + "unexpected node in varargslist: %d @ %d", + TYPE(ch), i); + goto error; + } + } + + return arguments(args, vararg, kwarg, defaults); + + error: + if (args) + asdl_seq_free(args); + if (defaults) + asdl_seq_free(defaults); + return NULL; +} + +static expr_ty +ast_for_dotted_name(struct compiling *c, const node *n) +{ + expr_ty e = NULL; + expr_ty attrib = NULL; + identifier id = NULL; + int i; + + REQ(n, dotted_name); + + id = NEW_IDENTIFIER(CHILD(n, 0)); + if (!id) + goto error; + e = Name(id, Load, LINENO(n)); + if (!e) + goto error; + id = NULL; + + for (i = 2; i < NCH(n); i+=2) { + id = NEW_IDENTIFIER(CHILD(n, i)); + if (!id) + goto error; + attrib = Attribute(e, id, Load, LINENO(CHILD(n, i))); + if (!attrib) + goto error; + e = attrib; + attrib = NULL; + } + + return e; + + error: + Py_XDECREF(id); + free_expr(e); + return NULL; +} + +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 = NULL; + + REQ(n, decorator); + + if ((NCH(n) < 3 && NCH(n) != 5 && NCH(n) != 6) + || TYPE(CHILD(n, 0)) != AT || TYPE(RCHILD(n, -1)) != NEWLINE) { + ast_error(n, "Invalid decorator node"); + goto error; + } + + name_expr = ast_for_dotted_name(c, CHILD(n, 1)); + if (!name_expr) + goto error; + + 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)); + if (!d) + goto error; + name_expr = NULL; + } + else { + d = ast_for_call(c, CHILD(n, 3), name_expr); + if (!d) + goto error; + name_expr = NULL; + } + + return d; + + error: + free_expr(name_expr); + free_expr(d); + return NULL; +} + +static asdl_seq* +ast_for_decorators(struct compiling *c, const node *n) +{ + asdl_seq* decorator_seq = NULL; + expr_ty d = NULL; + int i; + + REQ(n, decorators); + + decorator_seq = asdl_seq_new(NCH(n)); + if (!decorator_seq) + return NULL; + + for (i = 0; i < NCH(n); i++) { + d = ast_for_decorator(c, CHILD(n, i)); + if (!d) + goto error; + asdl_seq_APPEND(decorator_seq, d); + d = NULL; + } + return decorator_seq; + error: + asdl_expr_seq_free(decorator_seq); + free_expr(d); + return NULL; +} + +static stmt_ty +ast_for_funcdef(struct compiling *c, const node *n) +{ + /* funcdef: 'def' [decorators] NAME parameters ':' suite */ + identifier name = NULL; + arguments_ty args = NULL; + asdl_seq *body = NULL; + asdl_seq *decorator_seq = NULL; + int name_i; + + REQ(n, funcdef); + + if (NCH(n) == 6) { /* decorators are present */ + decorator_seq = ast_for_decorators(c, CHILD(n, 0)); + if (!decorator_seq) + goto error; + name_i = 2; + } + else { + name_i = 1; + } + + name = NEW_IDENTIFIER(CHILD(n, name_i)); + if (!name) + goto error; + else if (!strcmp(STR(CHILD(n, name_i)), "None")) { + ast_error(CHILD(n, name_i), "assignment to None"); + goto error; + } + args = ast_for_arguments(c, CHILD(n, name_i + 1)); + if (!args) + goto error; + body = ast_for_suite(c, CHILD(n, name_i + 3)); + if (!body) + goto error; + + return FunctionDef(name, args, body, decorator_seq, LINENO(n)); + +error: + asdl_stmt_seq_free(body); + asdl_expr_seq_free(decorator_seq); + free_arguments(args); + Py_XDECREF(name); + return NULL; +} + +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); + if (!args) + return NULL; + expression = ast_for_expr(c, CHILD(n, 2)); + if (!expression) { + free_arguments(args); + 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) { + free_arguments(args); + return NULL; + } + } + + return Lambda(args, expression, LINENO(n)); +} + +/* Count the number of 'for' loop in a list comprehension. + + Helper for ast_for_listcomp(). +*/ + +static int +count_list_fors(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; + } + else { + /* Should never be reached */ + PyErr_SetString(PyExc_Exception, "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(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(n); + if (n_fors == -1) + return NULL; + + listcomps = asdl_seq_new(n_fors); + if (!listcomps) { + free_expr(elt); + return NULL; + } + + ch = CHILD(n, 1); + for (i = 0; i < n_fors; i++) { + comprehension_ty lc; + asdl_seq *t; + expr_ty expression; + + REQ(ch, list_for); + + t = ast_for_exprlist(c, CHILD(ch, 1), Store); + if (!t) { + asdl_seq_free(listcomps); + free_expr(elt); + return NULL; + } + expression = ast_for_testlist(c, CHILD(ch, 3), 0); + if (!expression) { + asdl_seq_free(t); + asdl_seq_free(listcomps); + free_expr(elt); + return NULL; + } + + if (asdl_seq_LEN(t) == 1) + lc = comprehension(asdl_seq_GET(t, 0), expression, NULL); + else + lc = comprehension(Tuple(t, Store, LINENO(ch)), expression, NULL); + + if (!lc) { + asdl_seq_free(t); + asdl_seq_free(listcomps); + free_expr(expression); + free_expr(elt); + return NULL; + } + + if (NCH(ch) == 5) { + int j, n_ifs; + asdl_seq *ifs; + + ch = CHILD(ch, 4); + n_ifs = count_list_ifs(ch); + if (n_ifs == -1) { + asdl_seq_free(listcomps); + free_expr(elt); + return NULL; + } + + ifs = asdl_seq_new(n_ifs); + if (!ifs) { + asdl_seq_free(listcomps); + free_expr(elt); + return NULL; + } + + for (j = 0; j < n_ifs; j++) { + REQ(ch, list_iter); + + ch = CHILD(ch, 0); + REQ(ch, list_if); + + asdl_seq_APPEND(ifs, ast_for_expr(c, CHILD(ch, 1))); + 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_APPEND(listcomps, lc); + } + + return ListComp(elt, listcomps, LINENO(n)); +} + +/* + Count the number of 'for' loops in a generator expression. + + Helper for ast_for_genexp(). +*/ + +static int +count_gen_fors(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; + } + else { + /* Should never be reached */ + PyErr_SetString(PyExc_Exception, "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(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); + } +} + +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(n); + if (n_fors == -1) + return NULL; + + genexps = asdl_seq_new(n_fors); + if (!genexps) { + free_expr(elt); + return NULL; + } + + ch = CHILD(n, 1); + for (i = 0; i < n_fors; i++) { + comprehension_ty ge; + asdl_seq *t; + expr_ty expression; + + REQ(ch, gen_for); + + t = ast_for_exprlist(c, CHILD(ch, 1), Store); + if (!t) { + asdl_seq_free(genexps); + free_expr(elt); + return NULL; + } + expression = ast_for_testlist(c, CHILD(ch, 3), 1); + if (!expression) { + asdl_seq_free(genexps); + free_expr(elt); + return NULL; + } + + if (asdl_seq_LEN(t) == 1) + ge = comprehension(asdl_seq_GET(t, 0), expression, + NULL); + else + ge = comprehension(Tuple(t, Store, LINENO(ch)), + expression, NULL); + + if (!ge) { + asdl_seq_free(genexps); + free_expr(elt); + return NULL; + } + + if (NCH(ch) == 5) { + int j, n_ifs; + asdl_seq *ifs; + + ch = CHILD(ch, 4); + n_ifs = count_gen_ifs(ch); + if (n_ifs == -1) { + asdl_seq_free(genexps); + free_expr(elt); + return NULL; + } + + ifs = asdl_seq_new(n_ifs); + if (!ifs) { + asdl_seq_free(genexps); + free_expr(elt); + return NULL; + } + + for (j = 0; j < n_ifs; j++) { + REQ(ch, gen_iter); + ch = CHILD(ch, 0); + REQ(ch, gen_if); + + asdl_seq_APPEND(ifs, ast_for_expr(c, CHILD(ch, 1))); + 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_APPEND(genexps, ge); + } + + return GeneratorExp(elt, genexps, LINENO(n)); +} + +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. */ + return Name(NEW_IDENTIFIER(ch), Load, LINENO(n)); + case STRING: { + PyObject *str = parsestrplus(c, n); + + if (!str) + return NULL; + + return Str(str, LINENO(n)); + } + case NUMBER: { + PyObject *pynum = parsenumber(STR(ch)); + + if (!pynum) + return NULL; + + return Num(pynum, LINENO(n)); + } + case LPAR: /* some parenthesized expressions */ + ch = CHILD(n, 1); + + if (TYPE(ch) == RPAR) + return Tuple(NULL, Load, LINENO(n)); + + if (TYPE(ch) == yield_expr) + return ast_for_expr(c, ch); + + if ((NCH(ch) > 1) && (TYPE(CHILD(ch, 1)) == gen_for)) + return ast_for_genexp(c, ch); + + return ast_for_testlist(c, ch, 1); + case LSQB: /* list (or list comprehension) */ + ch = CHILD(n, 1); + + if (TYPE(ch) == RSQB) + return List(NULL, Load, LINENO(n)); + + 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)); + } + 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); + if (!keys) + return NULL; + + values = asdl_seq_new(size); + if (!values) { + asdl_seq_free(keys); + 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)); + } + case BACKQUOTE: { /* repr */ + expr_ty expression = ast_for_testlist(c, CHILD(n, 1), 0); + + if (!expression) + return NULL; + + return Repr(expression, LINENO(n)); + } + default: + PyErr_Format(PyExc_Exception, "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(); + + 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); + } + + 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) + /* XXX: If only 1 child, then should just be a colon. Should we + just skip assigning and just get to the return? */ + ch = CHILD(ch, 0); + else + ch = CHILD(ch, 1); + if (TYPE(ch) == test) { + step = ast_for_expr(c, ch); + if (!step) + return NULL; + } + } + + return Slice(lower, upper, step); +} + +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 operator; + + expr1 = ast_for_expr(c, CHILD(n, 0)); + if (!expr1) + return NULL; + + expr2 = ast_for_expr(c, CHILD(n, 2)); + if (!expr2) + return NULL; + + operator = get_operator(CHILD(n, 1)); + if (!operator) + return NULL; + + result = BinOp(expr1, operator, expr2, LINENO(n)); + 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); + + operator = get_operator(next_oper); + if (!operator) + return NULL; + + tmp = ast_for_expr(c, CHILD(n, i * 2 + 2)); + if (!tmp) + return NULL; + + tmp_result = BinOp(result, operator, tmp, + LINENO(next_oper)); + if (!tmp) + return NULL; + result = tmp_result; + } + return result; +} + +/* 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: and_test ('or' and_test)* | lambdef + 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)* + */ + + asdl_seq *seq; + int i; + + loop: + switch (TYPE(n)) { + case test: + if (TYPE(CHILD(n, 0)) == lambdef) + return ast_for_lambdef(c, CHILD(n, 0)); + /* Fall through to and_test */ + case and_test: + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto loop; + } + seq = asdl_seq_new((NCH(n) + 1) / 2); + 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)); + else { + assert(!strcmp(STR(CHILD(n, 1)), "or")); + return BoolOp(Or, seq, LINENO(n)); + } + break; + 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)); + } + case comparison: + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto loop; + } + else { + expr_ty expression; + asdl_seq *ops, *cmps; + ops = asdl_seq_new(NCH(n) / 2); + if (!ops) + return NULL; + cmps = asdl_seq_new(NCH(n) / 2); + if (!cmps) { + asdl_seq_free(ops); + return NULL; + } + for (i = 1; i < NCH(n); i += 2) { + /* XXX cmpop_ty is just an enum */ + cmpop_ty operator; + + operator = ast_for_comp_op(CHILD(n, i)); + if (!operator) + return NULL; + + expression = ast_for_expr(c, CHILD(n, i + 1)); + if (!expression) + return NULL; + + asdl_seq_SET(ops, i / 2, (void *)operator); + 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)); + } + 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), 0); + if (!exp) + return NULL; + } + return Yield(exp, LINENO(n)); + } + case factor: { + expr_ty expression; + + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto loop; + } + + 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)); + case MINUS: + return UnaryOp(USub, expression, LINENO(n)); + case TILDE: + return UnaryOp(Invert, expression, LINENO(n)); + } + break; + } + case power: { + expr_ty e = ast_for_atom(c, CHILD(n, 0)); + if (!e) + return NULL; + if (NCH(n) == 1) + return e; + /* power: atom trailer* ('**' factor)* + trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME + + XXX What about atom trailer trailer ** factor? + */ + for (i = 1; i < NCH(n); i++) { + expr_ty new = e; + node *ch = CHILD(n, i); + if (ch->n_str && strcmp(ch->n_str, "**") == 0) + break; + if (TYPE(CHILD(ch, 0)) == LPAR) { + if (NCH(ch) == 2) + new = Call(new, NULL, NULL, NULL, NULL, LINENO(ch)); + else + new = ast_for_call(c, CHILD(ch, 1), new); + + if (!new) { + free_expr(e); + return NULL; + } + } + else if (TYPE(CHILD(ch, 0)) == LSQB) { + REQ(CHILD(ch, 2), RSQB); + ch = CHILD(ch, 1); + if (NCH(ch) <= 2) { + slice_ty slc = ast_for_slice(c, CHILD(ch, 0)); + if (!slc) { + free_expr(e); + return NULL; + } + + new = Subscript(e, slc, Load, LINENO(ch)); + if (!new) { + free_expr(e); + free_slice(slc); + return NULL; + } + } + else { + int j; + slice_ty slc; + asdl_seq *slices = asdl_seq_new((NCH(ch) + 1) / 2); + if (!slices) { + free_expr(e); + return NULL; + } + + for (j = 0; j < NCH(ch); j += 2) { + slc = ast_for_slice(c, CHILD(ch, j)); + if (!slc) { + free_expr(e); + asdl_seq_free(slices); + return NULL; + } + asdl_seq_SET(slices, j / 2, slc); + } + new = Subscript(e, ExtSlice(slices), Load, LINENO(ch)); + if (!new) { + free_expr(e); + asdl_seq_free(slices); + return NULL; + } + } + } + else { + assert(TYPE(CHILD(ch, 0)) == DOT); + new = Attribute(e, NEW_IDENTIFIER(CHILD(ch, 1)), Load, + LINENO(ch)); + if (!new) { + free_expr(e); + return NULL; + } + } + e = new; + } + if (TYPE(CHILD(n, NCH(n) - 1)) == factor) { + expr_ty f = ast_for_expr(c, CHILD(n, NCH(n) - 1)); + if (!f) { + free_expr(e); + return NULL; + } + return BinOp(e, Pow, f, LINENO(n)); + } + return e; + } + default: + abort(); + PyErr_Format(PyExc_Exception, "unhandled expr: %d", TYPE(n)); + return NULL; + } + /* should never get here */ + 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 = NULL; + asdl_seq *keywords = NULL; + 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 parenthesised " + "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); + if (!args) + goto error; + keywords = asdl_seq_new(nkeywords); + if (!keywords) + goto error; + 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) { + e = ast_for_expr(c, CHILD(ch, 0)); + if (!e) + goto error; + asdl_seq_SET(args, nargs++, e); + } + else if (TYPE(CHILD(ch, 1)) == gen_for) { + e = ast_for_genexp(c, ch); + if (!e) + goto error; + asdl_seq_SET(args, nargs++, e); + } + else { + keyword_ty kw; + identifier key; + + /* CHILD(ch, 0) is test, but must be an identifier? */ + e = ast_for_expr(c, CHILD(ch, 0)); + if (!e) + goto error; + /* 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"); + goto error; + } else if (e->kind != Name_kind) { + ast_error(CHILD(ch, 0), "keyword can't be an expression"); + goto error; + } + key = e->v.Name.id; + free(e); + e = ast_for_expr(c, CHILD(ch, 2)); + if (!e) + goto error; + kw = keyword(key, e); + if (!kw) + goto error; + asdl_seq_SET(keywords, nkeywords++, kw); + } + } + else if (TYPE(ch) == STAR) { + vararg = ast_for_expr(c, CHILD(n, i+1)); + i++; + } + else if (TYPE(ch) == DOUBLESTAR) { + kwarg = ast_for_expr(c, CHILD(n, i+1)); + i++; + } + } + + return Call(func, args, keywords, vararg, kwarg, LINENO(n)); + + error: + if (args) + asdl_seq_free(args); + if (keywords) + asdl_seq_free(keywords); + return NULL; +} + +/* Unlike other ast_for_XXX() functions, this takes a flag that + indicates whether generator expressions are allowed. If gexp is + non-zero, check for testlist_gexp instead of plain testlist. +*/ + +static expr_ty +ast_for_testlist(struct compiling *c, const node* n, int gexp) +{ + /* testlist_gexp: test ( gen_for | (',' test)* [','] ) + testlist: test (',' test)* [','] + */ + + assert(NCH(n) > 0); + if (NCH(n) == 1) + return ast_for_expr(c, CHILD(n, 0)); + if (TYPE(CHILD(n, 1)) == gen_for) { + if (!gexp) { + ast_error(n, "illegal generator expression"); + return NULL; + } + return ast_for_genexp(c, n); + } + else { + asdl_seq *tmp = seq_for_testlist(c, n); + if (!tmp) + return NULL; + + return Tuple(tmp, Load, LINENO(n)); + } + return NULL; /* unreachable */ +} + +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), 0); + if (!e) + return NULL; + + return Expr(e, LINENO(n)); + } + else if (TYPE(CHILD(n, 1)) == augassign) { + expr_ty expr1, expr2; + operator_ty operator; + node *ch = CHILD(n, 0); + + if (TYPE(ch) == testlist) + expr1 = ast_for_testlist(c, ch, 0); + else + expr1 = Yield(ast_for_expr(c, CHILD(ch, 0)), LINENO(ch)); + + if (!expr1) + return NULL; + if (expr1->kind == GeneratorExp_kind) { + ast_error(ch, "augmented assignment to generator " + "expression not possible"); + return NULL; + } + if (expr1->kind == Name_kind) { + char *var_name = PyString_AS_STRING(expr1->v.Name.id); + if (var_name[0] == 'N' && !strcmp(var_name, "None")) { + ast_error(ch, "assignment to None"); + return NULL; + } + } + + ch = CHILD(n, 2); + if (TYPE(ch) == testlist) + expr2 = ast_for_testlist(c, ch, 0); + else + expr2 = Yield(ast_for_expr(c, ch), LINENO(ch)); + if (!expr2) + return NULL; + + operator = ast_for_augassign(CHILD(n, 1)); + if (!operator) + return NULL; + + return AugAssign(expr1, operator, expr2, LINENO(n)); + } + 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); + if (!targets) + return NULL; + for (i = 0; i < NCH(n) - 2; i += 2) { + node *ch = CHILD(n, i); + if (TYPE(ch) == yield_expr) { + ast_error(ch, "assignment to yield expression not possible"); + goto error; + } + expr_ty e = ast_for_testlist(c, ch, 0); + + /* set context to assign */ + if (!e) + goto error; + + if (!set_context(e, Store, CHILD(n, i))) { + free_expr(e); + goto error; + } + + asdl_seq_SET(targets, i / 2, e); + } + value = CHILD(n, NCH(n) - 1); + if (TYPE(value) == testlist) + expression = ast_for_testlist(c, value, 0); + else + expression = ast_for_expr(c, value); + if (!expression) + return NULL; + return Assign(targets, expression, LINENO(n)); + error: + for (i = i / 2; i >= 0; i--) + free_expr((expr_ty)asdl_seq_GET(targets, i)); + asdl_seq_free(targets); + return NULL; + } + return NULL; +} + +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; + bool nl; + int i, 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; + } + seq = asdl_seq_new((NCH(n) + 1 - start) / 2); + if (!seq) + return NULL; + for (i = start; i < NCH(n); i += 2) { + expression = ast_for_expr(c, CHILD(n, i)); + if (!expression) { + asdl_seq_free(seq); + return NULL; + } + + asdl_seq_APPEND(seq, expression); + } + nl = (TYPE(CHILD(n, NCH(n) - 1)) == COMMA) ? false : true; + return Print(dest, seq, nl, LINENO(n)); +} + +static asdl_seq * +ast_for_exprlist(struct compiling *c, const node *n, int context) +{ + asdl_seq *seq; + int i; + expr_ty e; + + REQ(n, exprlist); + + seq = asdl_seq_new((NCH(n) + 1) / 2); + if (!seq) + return NULL; + for (i = 0; i < NCH(n); i += 2) { + e = ast_for_expr(c, CHILD(n, i)); + if (!e) { + asdl_seq_free(seq); + return NULL; + } + if (context) { + if (!set_context(e, context, CHILD(n, i))) + return NULL; + } + asdl_seq_SET(seq, i / 2, e); + } + 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)); +} + +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)); + case continue_stmt: + return Continue(LINENO(n)); + 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)); + } + case return_stmt: + if (NCH(ch) == 1) + return Return(NULL, LINENO(n)); + else { + expr_ty expression = ast_for_testlist(c, CHILD(ch, 1), 0); + if (!expression) + return NULL; + return Return(expression, LINENO(n)); + } + case raise_stmt: + if (NCH(ch) == 1) + return Raise(NULL, NULL, NULL, LINENO(n)); + 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)); + } + 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)); + } + 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)); + } + default: + PyErr_Format(PyExc_Exception, + "unexpected flow_stmt: %d", TYPE(ch)); + return NULL; + } +} + +static alias_ty +alias_for_import_name(const node *n) +{ + /* + import_as_name: NAME [NAME NAME] + dotted_as_name: dotted_name [NAME NAME] + dotted_name: NAME ('.' NAME)* + */ + loop: + switch (TYPE(n)) { + case import_as_name: + if (NCH(n) == 3) + return alias(NEW_IDENTIFIER(CHILD(n, 0)), + NEW_IDENTIFIER(CHILD(n, 2))); + else + return alias(NEW_IDENTIFIER(CHILD(n, 0)), + NULL); + break; + case dotted_as_name: + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto loop; + } + else { + alias_ty a = alias_for_import_name(CHILD(n, 0)); + assert(!a->asname); + a->asname = NEW_IDENTIFIER(CHILD(n, 2)); + return a; + } + break; + case dotted_name: + if (NCH(n) == 1) + return alias(NEW_IDENTIFIER(CHILD(n, 0)), NULL); + else { + /* Create a string of the form "a.b.c" */ + int i, len; + PyObject *str; + 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); + return alias(str, NULL); + } + break; + case STAR: + return alias(PyString_InternFromString("*"), NULL); + default: + PyErr_Format(PyExc_Exception, + "unexpected import name: %d", TYPE(n)); + return NULL; + } + 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 i; + asdl_seq *aliases; + + REQ(n, import_stmt); + n = CHILD(n, 0); + if (STR(CHILD(n, 0))[0] == 'i') { /* import */ + n = CHILD(n, 1); + aliases = asdl_seq_new((NCH(n) + 1) / 2); + if (!aliases) + return NULL; + for (i = 0; i < NCH(n); i += 2) { + alias_ty import_alias = alias_for_import_name(CHILD(n, i)); + if (!import_alias) { + asdl_seq_free(aliases); + return NULL; + } + asdl_seq_SET(aliases, i / 2, import_alias); + } + return Import(aliases, LINENO(n)); + } + else if (STR(CHILD(n, 0))[0] == 'f') { /* from */ + stmt_ty import; + int n_children; + const char *from_modules; + int lineno = LINENO(n); + alias_ty mod = alias_for_import_name(CHILD(n, 1)); + if (!mod) + return NULL; + + /* XXX this needs to be cleaned up */ + + from_modules = STR(CHILD(n, 3)); + if (!from_modules) { + n = CHILD(n, 3); /* from ... import x, y, z */ + if (NCH(n) % 2 == 0) { + /* it ends with a comma, not valid but the parser allows it */ + ast_error(n, "trailing comma not allowed without" + " surrounding parentheses"); + return NULL; + } + } + else if (from_modules[0] == '*') { + n = CHILD(n, 3); /* from ... import * */ + } + else if (from_modules[0] == '(') + n = CHILD(n, 4); /* from ... import (x, y, z) */ + else + return NULL; + + n_children = NCH(n); + if (from_modules && from_modules[0] == '*') + n_children = 1; + + aliases = asdl_seq_new((n_children + 1) / 2); + if (!aliases) { + free_alias(mod); + return NULL; + } + + /* handle "from ... import *" special b/c there's no children */ + if (from_modules && from_modules[0] == '*') { + alias_ty import_alias = alias_for_import_name(n); + if (!import_alias) { + asdl_seq_free(aliases); + free_alias(mod); + return NULL; + } + asdl_seq_APPEND(aliases, import_alias); + } + + for (i = 0; i < NCH(n); i += 2) { + alias_ty import_alias = alias_for_import_name(CHILD(n, i)); + if (!import_alias) { + asdl_seq_free(aliases); + free_alias(mod); + return NULL; + } + asdl_seq_APPEND(aliases, import_alias); + } + Py_INCREF(mod->name); + import = ImportFrom(mod->name, aliases, lineno); + free_alias(mod); + return import; + } + PyErr_Format(PyExc_Exception, + "unknown import statement: starts with command '%s'", + STR(CHILD(n, 0))); + return NULL; +} + +static stmt_ty +ast_for_global_stmt(struct compiling *c, const node *n) +{ + /* global_stmt: 'global' NAME (',' NAME)* */ + identifier name; + asdl_seq *s; + int i; + + REQ(n, global_stmt); + s = asdl_seq_new(NCH(n) / 2); + if (!s) + return NULL; + for (i = 1; i < NCH(n); i += 2) { + name = NEW_IDENTIFIER(CHILD(n, i)); + if (!name) { + asdl_seq_free(s); + return NULL; + } + asdl_seq_SET(s, i / 2, name); + } + return Global(s, LINENO(n)); +} + +static stmt_ty +ast_for_exec_stmt(struct compiling *c, const node *n) +{ + expr_ty expr1, globals = NULL, locals = NULL; + int n_children = NCH(n); + if (n_children != 2 && n_children != 4 && n_children != 6) { + PyErr_Format(PyExc_Exception, + "poorly formed 'exec' statement: %d parts to statement", + n_children); + return NULL; + } + + /* exec_stmt: 'exec' expr ['in' test [',' test]] */ + REQ(n, exec_stmt); + expr1 = ast_for_expr(c, CHILD(n, 1)); + if (!expr1) + return NULL; + if (n_children >= 4) { + globals = ast_for_expr(c, CHILD(n, 3)); + if (!globals) + return NULL; + } + if (n_children == 6) { + locals = ast_for_expr(c, CHILD(n, 5)); + if (!locals) + return NULL; + } + + return Exec(expr1, globals, locals, LINENO(n)); +} + +static stmt_ty +ast_for_assert_stmt(struct compiling *c, const node *n) +{ + /* assert_stmt: 'assert' test [',' test] */ + REQ(n, assert_stmt); + if (NCH(n) == 2) { + expr_ty expression = ast_for_expr(c, CHILD(n, 1)); + if (!expression) + return NULL; + return Assert(expression, NULL, LINENO(n)); + } + else if (NCH(n) == 4) { + expr_ty expr1, expr2; + + expr1 = ast_for_expr(c, CHILD(n, 1)); + if (!expr1) + return NULL; + expr2 = ast_for_expr(c, CHILD(n, 3)); + if (!expr2) + return NULL; + + return Assert(expr1, expr2, LINENO(n)); + } + PyErr_Format(PyExc_Exception, + "improper number of parts to 'assert' statement: %d", + NCH(n)); + return NULL; +} + +static asdl_seq * +ast_for_suite(struct compiling *c, const node *n) +{ + /* suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT */ + asdl_seq *seq = NULL; + stmt_ty s; + int i, total, num, end, pos = 0; + node *ch; + + REQ(n, suite); + + total = num_stmts(n); + seq = asdl_seq_new(total); + if (!seq) + return NULL; + if (TYPE(CHILD(n, 0)) == simple_stmt) { + n = CHILD(n, 0); + /* simple_stmt always ends with a NEWLINE, + and may have a trailing SEMI + */ + end = NCH(n) - 1; + if (TYPE(CHILD(n, end - 1)) == SEMI) + end--; + /* loop by 2 to skip semi-colons */ + for (i = 0; i < end; i += 2) { + ch = CHILD(n, i); + s = ast_for_stmt(c, ch); + if (!s) + goto error; + asdl_seq_SET(seq, pos++, s); + } + } + else { + for (i = 2; i < (NCH(n) - 1); i++) { + ch = CHILD(n, i); + REQ(ch, stmt); + num = num_stmts(ch); + if (num == 1) { + /* small_stmt or compound_stmt with only one child */ + s = ast_for_stmt(c, ch); + if (!s) + goto error; + asdl_seq_SET(seq, pos++, s); + } + else { + int j; + ch = CHILD(ch, 0); + REQ(ch, simple_stmt); + for (j = 0; j < NCH(ch); j += 2) { + s = ast_for_stmt(c, CHILD(ch, j)); + if (!s) + goto error; + asdl_seq_SET(seq, pos++, s); + } + } + } + } + assert(pos == seq->size); + return seq; + error: + if (seq) + asdl_seq_free(seq); + return NULL; +} + +static stmt_ty +ast_for_if_stmt(struct compiling *c, const node *n) +{ + /* if_stmt: 'if' test ':' suite ('elif' test ':' suite)* + ['else' ':' suite] + */ + char *s; + + REQ(n, if_stmt); + + if (NCH(n) == 4) { + expr_ty expression; + asdl_seq *suite_seq; + + expression = ast_for_expr(c, CHILD(n, 1)); + if (!expression) + return NULL; + suite_seq = ast_for_suite(c, CHILD(n, 3)); + if (!suite_seq) + return NULL; + + return If(expression, suite_seq, NULL, LINENO(n)); + } + s = STR(CHILD(n, 4)); + /* s[2], the third character in the string, will be + 's' for el_s_e, or + 'i' for el_i_f + */ + if (s[2] == 's') { + expr_ty expression; + asdl_seq *seq1, *seq2; + + expression = ast_for_expr(c, CHILD(n, 1)); + if (!expression) + return NULL; + seq1 = ast_for_suite(c, CHILD(n, 3)); + if (!seq1) + return NULL; + seq2 = ast_for_suite(c, CHILD(n, 6)); + if (!seq2) + return NULL; + + return If(expression, seq1, seq2, LINENO(n)); + } + else if (s[2] == 'i') { + int i, n_elif, has_else = 0; + asdl_seq *orelse = NULL; + n_elif = NCH(n) - 4; + /* must reference the child n_elif+1 since 'else' token is third, + not fourth, child from the end. */ + if (TYPE(CHILD(n, (n_elif + 1))) == NAME + && STR(CHILD(n, (n_elif + 1)))[2] == 's') { + has_else = 1; + n_elif -= 3; + } + n_elif /= 4; + + if (has_else) { + expr_ty expression; + asdl_seq *seq1, *seq2; + + orelse = asdl_seq_new(1); + if (!orelse) + return NULL; + expression = ast_for_expr(c, CHILD(n, NCH(n) - 6)); + if (!expression) { + asdl_seq_free(orelse); + return NULL; + } + seq1 = ast_for_suite(c, CHILD(n, NCH(n) - 4)); + if (!seq1) { + asdl_seq_free(orelse); + return NULL; + } + seq2 = ast_for_suite(c, CHILD(n, NCH(n) - 1)); + if (!seq2) { + asdl_seq_free(orelse); + return NULL; + } + + asdl_seq_SET(orelse, 0, If(expression, seq1, seq2, + LINENO(CHILD(n, NCH(n) - 6)))); + /* the just-created orelse handled the last elif */ + n_elif--; + } + else + orelse = NULL; + + for (i = 0; i < n_elif; i++) { + int off = 5 + (n_elif - i - 1) * 4; + expr_ty expression; + asdl_seq *suite_seq; + asdl_seq *new = asdl_seq_new(1); + if (!new) + return NULL; + expression = ast_for_expr(c, CHILD(n, off)); + if (!expression) { + asdl_seq_free(new); + return NULL; + } + suite_seq = ast_for_suite(c, CHILD(n, off + 2)); + if (!suite_seq) { + asdl_seq_free(new); + return NULL; + } + + asdl_seq_SET(new, 0, + If(expression, suite_seq, orelse, + LINENO(CHILD(n, off)))); + orelse = new; + } + return If(ast_for_expr(c, CHILD(n, 1)), + ast_for_suite(c, CHILD(n, 3)), + orelse, LINENO(n)); + } + else { + PyErr_Format(PyExc_Exception, + "unexpected token in 'if' statement: %s", s); + return NULL; + } +} + +static stmt_ty +ast_for_while_stmt(struct compiling *c, const node *n) +{ + /* while_stmt: 'while' test ':' suite ['else' ':' suite] */ + REQ(n, while_stmt); + + if (NCH(n) == 4) { + expr_ty expression; + asdl_seq *suite_seq; + + expression = ast_for_expr(c, CHILD(n, 1)); + if (!expression) + return NULL; + suite_seq = ast_for_suite(c, CHILD(n, 3)); + if (!suite_seq) + return NULL; + return While(expression, suite_seq, NULL, LINENO(n)); + } + else if (NCH(n) == 7) { + expr_ty expression; + asdl_seq *seq1, *seq2; + + expression = ast_for_expr(c, CHILD(n, 1)); + if (!expression) + return NULL; + seq1 = ast_for_suite(c, CHILD(n, 3)); + if (!seq1) + return NULL; + seq2 = ast_for_suite(c, CHILD(n, 6)); + if (!seq2) + return NULL; + + return While(expression, seq1, seq2, LINENO(n)); + } + else { + PyErr_Format(PyExc_Exception, + "wrong number of tokens for 'while' statement: %d", + NCH(n)); + return NULL; + } +} + +static stmt_ty +ast_for_for_stmt(struct compiling *c, const node *n) +{ + asdl_seq *_target = NULL, *seq = NULL, *suite_seq = NULL; + expr_ty expression; + expr_ty target; + /* for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] */ + REQ(n, for_stmt); + + if (NCH(n) == 9) { + seq = ast_for_suite(c, CHILD(n, 8)); + if (!seq) + return NULL; + } + + _target = ast_for_exprlist(c, CHILD(n, 1), Store); + if (!_target) + return NULL; + if (asdl_seq_LEN(_target) == 1) { + target = asdl_seq_GET(_target, 0); + asdl_seq_free(_target); + } + else + target = Tuple(_target, Store, LINENO(n)); + + expression = ast_for_testlist(c, CHILD(n, 3), 0); + if (!expression) + return NULL; + suite_seq = ast_for_suite(c, CHILD(n, 5)); + if (!suite_seq) + return NULL; + + return For(target, expression, suite_seq, seq, LINENO(n)); +} + +static excepthandler_ty +ast_for_except_clause(struct compiling *c, const node *exc, node *body) +{ + /* except_clause: 'except' [test [',' test]] */ + REQ(exc, except_clause); + REQ(body, suite); + + if (NCH(exc) == 1) { + asdl_seq *suite_seq = ast_for_suite(c, body); + if (!suite_seq) + return NULL; + + return excepthandler(NULL, NULL, suite_seq); + } + else if (NCH(exc) == 2) { + expr_ty expression; + asdl_seq *suite_seq; + + expression = ast_for_expr(c, CHILD(exc, 1)); + if (!expression) + return NULL; + suite_seq = ast_for_suite(c, body); + if (!suite_seq) + return NULL; + + return excepthandler(expression, NULL, suite_seq); + } + else if (NCH(exc) == 4) { + asdl_seq *suite_seq; + expr_ty expression; + expr_ty e = ast_for_expr(c, CHILD(exc, 3)); + if (!e) + return NULL; + if (!set_context(e, Store, CHILD(exc, 3))) + return NULL; + expression = ast_for_expr(c, CHILD(exc, 1)); + if (!expression) + return NULL; + suite_seq = ast_for_suite(c, body); + if (!suite_seq) + return NULL; + + return excepthandler(expression, e, suite_seq); + } + else { + PyErr_Format(PyExc_Exception, + "wrong number of children for 'except' clause: %d", + NCH(exc)); + return NULL; + } +} + +static stmt_ty +ast_for_try_stmt(struct compiling *c, const node *n) +{ + REQ(n, try_stmt); + + if (TYPE(CHILD(n, 3)) == NAME) {/* must be 'finally' */ + /* try_stmt: 'try' ':' suite 'finally' ':' suite) */ + asdl_seq *s1, *s2; + s1 = ast_for_suite(c, CHILD(n, 2)); + if (!s1) + return NULL; + s2 = ast_for_suite(c, CHILD(n, 5)); + if (!s2) + return NULL; + + return TryFinally(s1, s2, LINENO(n)); + } + else if (TYPE(CHILD(n, 3)) == except_clause) { + /* try_stmt: ('try' ':' suite (except_clause ':' suite)+ + ['else' ':' suite] + */ + asdl_seq *suite_seq1, *suite_seq2; + asdl_seq *handlers; + int i, has_else = 0, n_except = NCH(n) - 3; + if (TYPE(CHILD(n, NCH(n) - 3)) == NAME) { + has_else = 1; + n_except -= 3; + } + n_except /= 3; + handlers = asdl_seq_new(n_except); + if (!handlers) + return NULL; + for (i = 0; i < n_except; i++) { + excepthandler_ty e = ast_for_except_clause(c, + CHILD(n, 3 + i * 3), + CHILD(n, 5 + i * 3)); + if (!e) + return NULL; + asdl_seq_SET(handlers, i, e); + } + + suite_seq1 = ast_for_suite(c, CHILD(n, 2)); + if (!suite_seq1) + return NULL; + if (has_else) { + suite_seq2 = ast_for_suite(c, CHILD(n, NCH(n) - 1)); + if (!suite_seq2) + return NULL; + } + else + suite_seq2 = NULL; + + return TryExcept(suite_seq1, handlers, suite_seq2, LINENO(n)); + } + else { + PyErr_SetString(PyExc_Exception, "malformed 'try' statement"); + return NULL; + } +} + +static stmt_ty +ast_for_classdef(struct compiling *c, const node *n) +{ + /* classdef: 'class' NAME ['(' testlist ')'] ':' suite */ + expr_ty _bases; + asdl_seq *bases, *s; + + REQ(n, classdef); + + if (!strcmp(STR(CHILD(n, 1)), "None")) { + ast_error(n, "assignment to None"); + return NULL; + } + + if (NCH(n) == 4) { + s = ast_for_suite(c, CHILD(n, 3)); + if (!s) + return NULL; + return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, s, LINENO(n)); + } + /* check for empty base list */ + if (TYPE(CHILD(n,3)) == RPAR) { + s = ast_for_suite(c, CHILD(n,5)); + if (!s) + return NULL; + return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, s, LINENO(n)); + } + + /* else handle the base class list */ + _bases = ast_for_testlist(c, CHILD(n, 3), 0); + if (!_bases) + return NULL; + /* XXX: I don't think we can set to diff types here, how to free??? + + Here's the allocation chain: + Tuple (Python-ast.c:907) + ast_for_testlist (ast.c:1782) + ast_for_classdef (ast.c:2677) + */ + if (_bases->kind == Tuple_kind) + bases = _bases->v.Tuple.elts; + else { + bases = asdl_seq_new(1); + if (!bases) { + free_expr(_bases); + /* XXX: free _bases */ + return NULL; + } + asdl_seq_SET(bases, 0, _bases); + } + + s = ast_for_suite(c, CHILD(n, 6)); + if (!s) { + /* XXX: I think this free is correct, but needs to change see above */ + if (_bases->kind == Tuple_kind) + free_expr(_bases); + else { + free_expr(_bases); + asdl_seq_free(bases); + } + return NULL; + } + return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), bases, s, LINENO(n)); +} + +static stmt_ty +ast_for_stmt(struct compiling *c, const node *n) +{ + if (TYPE(n) == stmt) { + assert(NCH(n) == 1); + n = CHILD(n, 0); + } + if (TYPE(n) == simple_stmt) { + assert(num_stmts(n) == 1); + n = CHILD(n, 0); + } + if (TYPE(n) == small_stmt) { + REQ(n, small_stmt); + n = CHILD(n, 0); + /* small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt + | flow_stmt | import_stmt | global_stmt | exec_stmt + | assert_stmt + */ + switch (TYPE(n)) { + case expr_stmt: + return ast_for_expr_stmt(c, n); + case print_stmt: + return ast_for_print_stmt(c, n); + case del_stmt: + return ast_for_del_stmt(c, n); + case pass_stmt: + return Pass(LINENO(n)); + case flow_stmt: + return ast_for_flow_stmt(c, n); + case import_stmt: + return ast_for_import_stmt(c, n); + case global_stmt: + return ast_for_global_stmt(c, n); + case exec_stmt: + return ast_for_exec_stmt(c, n); + case assert_stmt: + return ast_for_assert_stmt(c, n); + default: + PyErr_Format(PyExc_Exception, + "unhandled small_stmt: TYPE=%d NCH=%d\n", + TYPE(n), NCH(n)); + return NULL; + } + } + else { + /* compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt + | funcdef | classdef + */ + node *ch = CHILD(n, 0); + REQ(n, compound_stmt); + switch (TYPE(ch)) { + case if_stmt: + return ast_for_if_stmt(c, ch); + case while_stmt: + return ast_for_while_stmt(c, ch); + case for_stmt: + return ast_for_for_stmt(c, ch); + case try_stmt: + return ast_for_try_stmt(c, ch); + case funcdef: + return ast_for_funcdef(c, ch); + case classdef: + return ast_for_classdef(c, ch); + default: + PyErr_Format(PyExc_Exception, + "unhandled small_stmt: TYPE=%d NCH=%d\n", + TYPE(n), NCH(n)); + return NULL; + } + } +} + +static PyObject * +parsenumber(const char *s) +{ + const char *end; + long x; + double dx; +#ifndef WITHOUT_COMPLEX + Py_complex c; + int imflag; +#endif + + errno = 0; + end = s + strlen(s) - 1; +#ifndef WITHOUT_COMPLEX + imflag = *end == 'j' || *end == 'J'; +#endif + if (*end == 'l' || *end == 'L') + return PyLong_FromString((char *)s, (char **)0, 0); + if (s[0] == '0') { + x = (long) PyOS_strtoul((char *)s, (char **)&end, 0); + if (x < 0 && errno == 0) { + return PyLong_FromString((char *)s, + (char **)0, + 0); + } + } + else + x = PyOS_strtol((char *)s, (char **)&end, 0); + if (*end == '\0') { + if (errno != 0) + return PyLong_FromString((char *)s, (char **)0, 0); + return PyInt_FromLong(x); + } + /* XXX Huge floats may silently fail */ +#ifndef WITHOUT_COMPLEX + if (imflag) { + c.real = 0.; + PyFPE_START_PROTECT("atof", return 0) + c.imag = atof(s); + PyFPE_END_PROTECT(c) + return PyComplex_FromCComplex(c); + } + else +#endif + { + PyFPE_START_PROTECT("atof", return 0) + dx = atof(s); + PyFPE_END_PROTECT(dx) + return PyFloat_FromDouble(dx); + } +} + +static PyObject * +decode_utf8(const char **sPtr, const char *end, char* encoding) +{ +#ifndef Py_USING_UNICODE + Py_FatalError("decode_utf8 should not be called in this build."); + return NULL; +#else + PyObject *u, *v; + char *s, *t; + t = s = (char *)*sPtr; + /* while (s < end && *s != '\\') s++; */ /* inefficient for u".." */ + while (s < end && (*s & 0x80)) s++; + *sPtr = s; + u = PyUnicode_DecodeUTF8(t, s - t, NULL); + if (u == NULL) + return NULL; + v = PyUnicode_AsEncodedString(u, encoding, NULL); + Py_DECREF(u); + return v; +#endif +} + +static PyObject * +decode_unicode(const char *s, size_t len, int rawmode, const char *encoding) +{ + PyObject *v, *u; + char *buf; + char *p; + const char *end; + if (encoding == NULL) { + buf = (char *)s; + u = NULL; + } else if (strcmp(encoding, "iso-8859-1") == 0) { + buf = (char *)s; + u = NULL; + } else { + /* "\XX" may become "\u005c\uHHLL" (12 bytes) */ + u = PyString_FromStringAndSize((char *)NULL, len * 4); + if (u == NULL) + return NULL; + p = buf = PyString_AsString(u); + end = s + len; + while (s < end) { + if (*s == '\\') { + *p++ = *s++; + if (*s & 0x80) { + strcpy(p, "u005c"); + p += 5; + } + } + if (*s & 0x80) { /* XXX inefficient */ + PyObject *w; + char *r; + int rn, i; + w = decode_utf8(&s, end, "utf-16-be"); + if (w == NULL) { + Py_DECREF(u); + return NULL; + } + r = PyString_AsString(w); + rn = PyString_Size(w); + assert(rn % 2 == 0); + for (i = 0; i < rn; i += 2) { + sprintf(p, "\\u%02x%02x", + r[i + 0] & 0xFF, + r[i + 1] & 0xFF); + p += 6; + } + Py_DECREF(w); + } else { + *p++ = *s++; + } + } + len = p - buf; + s = buf; + } + if (rawmode) + v = PyUnicode_DecodeRawUnicodeEscape(s, len, NULL); + else + v = PyUnicode_DecodeUnicodeEscape(s, len, NULL); + Py_XDECREF(u); + return v; +} + +/* s is a Python string literal, including the bracketing quote characters, + * and r &/or u prefixes (if any), and embedded escape sequences (if any). + * parsestr parses it, and returns the decoded Python string object. + */ +static PyObject * +parsestr(const char *s, const char *encoding) +{ + PyObject *v; + size_t len; + int quote = *s; + int rawmode = 0; + int need_encoding; + int unicode = 0; + + if (isalpha(quote) || quote == '_') { + if (quote == 'u' || quote == 'U') { + quote = *++s; + unicode = 1; + } + if (quote == 'r' || quote == 'R') { + quote = *++s; + rawmode = 1; + } + } + if (quote != '\'' && quote != '\"') { + PyErr_BadInternalCall(); + return NULL; + } + s++; + len = strlen(s); + if (len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "string to parse is too long"); + return NULL; + } + if (s[--len] != quote) { + PyErr_BadInternalCall(); + return NULL; + } + if (len >= 4 && s[0] == quote && s[1] == quote) { + s += 2; + len -= 2; + if (s[--len] != quote || s[--len] != quote) { + PyErr_BadInternalCall(); + return NULL; + } + } +#ifdef Py_USING_UNICODE + if (unicode || Py_UnicodeFlag) { + return decode_unicode(s, len, rawmode, encoding); + } +#endif + need_encoding = (encoding != NULL && + strcmp(encoding, "utf-8") != 0 && + strcmp(encoding, "iso-8859-1") != 0); + if (rawmode || strchr(s, '\\') == NULL) { + if (need_encoding) { +#ifndef Py_USING_UNICODE + /* This should not happen - we never see any other + encoding. */ + Py_FatalError("cannot deal with encodings in this build."); +#else + PyObject* u = PyUnicode_DecodeUTF8(s, len, NULL); + if (u == NULL) + return NULL; + v = PyUnicode_AsEncodedString(u, encoding, NULL); + Py_DECREF(u); + return v; +#endif + } else { + return PyString_FromStringAndSize(s, len); + } + } + + v = PyString_DecodeEscape(s, len, NULL, unicode, + need_encoding ? encoding : NULL); + return v; +} + +/* Build a Python string object out of a STRING atom. This takes care of + * compile-time literal catenation, calling parsestr() on each piece, and + * pasting the intermediate results together. + */ +static PyObject * +parsestrplus(struct compiling *c, const node *n) +{ + PyObject *v; + int i; + REQ(CHILD(n, 0), STRING); + if ((v = parsestr(STR(CHILD(n, 0)), c->c_encoding)) != NULL) { + /* String literal concatenation */ + for (i = 1; i < NCH(n); i++) { + PyObject *s; + s = parsestr(STR(CHILD(n, i)), c->c_encoding); + if (s == NULL) + goto onError; + if (PyString_Check(v) && PyString_Check(s)) { + PyString_ConcatAndDel(&v, s); + if (v == NULL) + goto onError; + } +#ifdef Py_USING_UNICODE + else { + PyObject *temp; + temp = PyUnicode_Concat(v, s); + Py_DECREF(s); + if (temp == NULL) + goto onError; + Py_DECREF(v); + v = temp; + } +#endif + } + } + return v; + + onError: + Py_XDECREF(v); + return NULL; +} diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 3e5f4eb..2d51531 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1,9 +1,9 @@ - /* Built-in functions */ #include "Python.h" #include "node.h" +#include "code.h" #include "compile.h" #include "eval.h" diff --git a/Python/ceval.c b/Python/ceval.c index 38b1328..fdfe83e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -8,7 +8,7 @@ #include "Python.h" -#include "compile.h" +#include "code.h" #include "frameobject.h" #include "eval.h" #include "opcode.h" @@ -543,7 +543,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw) #ifdef LLTRACE int lltrace; #endif -#if defined(Py_DEBUG) || defined(LLTRACE) +#if defined(Py_DEBUG) /* Make it easier to find out where we are with a debugger */ char *filename; #endif @@ -743,9 +743,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw) f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ #ifdef LLTRACE - lltrace = PyDict_GetItemString(f->f_globals,"__lltrace__") != NULL; + lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL; #endif -#if defined(Py_DEBUG) || defined(LLTRACE) +#if defined(Py_DEBUG) filename = PyString_AsString(co->co_filename); #endif @@ -2257,23 +2257,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throw) case MAKE_CLOSURE: { - int nfree; v = POP(); /* code object */ x = PyFunction_New(v, f->f_globals); - nfree = PyCode_GetNumFree((PyCodeObject *)v); Py_DECREF(v); - /* XXX Maybe this should be a separate opcode? */ - if (x != NULL && nfree > 0) { - v = PyTuple_New(nfree); - if (v == NULL) { - Py_DECREF(x); - x = NULL; - break; - } - while (--nfree >= 0) { - w = POP(); - PyTuple_SET_ITEM(v, nfree, w); - } + if (x != NULL) { + v = POP(); err = PyFunction_SetClosure(x, v); Py_DECREF(v); } @@ -2695,12 +2683,18 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, if (co->co_flags & CO_VARKEYWORDS) nargs++; - /* Check for cells that shadow args */ - for (i = 0; i < f->f_ncells && j < nargs; ++i) { + /* Initialize each cell var, taking into account + cell vars that are initialized from arguments. + + Should arrange for the compiler to put cellvars + that are arguments at the beginning of the cellvars + list so that we can march over it more efficiently? + */ + for (i = 0; i < f->f_ncells; ++i) { cellname = PyString_AS_STRING( PyTuple_GET_ITEM(co->co_cellvars, i)); found = 0; - while (j < nargs) { + for (j = 0; j < nargs; j++) { argname = PyString_AS_STRING( PyTuple_GET_ITEM(co->co_varnames, j)); if (strcmp(cellname, argname) == 0) { @@ -2711,7 +2705,6 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, found = 1; break; } - j++; } if (found == 0) { c = PyCell_New(NULL); @@ -2720,14 +2713,6 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, SETLOCAL(f->f_nlocals + i, c); } } - /* Initialize any that are left */ - while (i < f->f_ncells) { - c = PyCell_New(NULL); - if (c == NULL) - goto fail; - SETLOCAL(f->f_nlocals + i, c); - i++; - } } if (f->f_nfreevars) { int i; diff --git a/Python/compile.c b/Python/compile.c index 99ccf29..10c94e7 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1,385 +1,379 @@ -/* Compile an expression node to intermediate code */ - -/* XXX TO DO: - XXX add __doc__ attribute == co_doc to code object attributes? - XXX (it's currently the first item of the co_const tuple) - XXX Generate simple jump for break/return outside 'try...finally' - XXX Allow 'continue' inside finally clause of try-finally - XXX New opcode for loading the initial index for a for loop - XXX other JAR tricks? -*/ +/* + * This file compiles an abstract syntax tree (AST) into Python bytecode. + * + * The primary entry point is PyAST_Compile(), which returns a + * PyCodeObject. The compiler makes several passes to build the code + * object: + * 1. Checks for future statements. See future.c + * 2. Builds a symbol table. See symtable.c. + * 3. Generate code for basic blocks. See compiler_mod() in this file. + * 4. Assemble the basic blocks into final code. See assemble() in + * this file. + * + * Note that compiler_mod() suggests module, but the module ast type + * (mod_ty) has cases for expressions and interactive statements. + */ #include "Python.h" +#include "Python-ast.h" #include "node.h" -#include "token.h" -#include "graminit.h" +#include "ast.h" +#include "code.h" #include "compile.h" #include "symtable.h" #include "opcode.h" -#include "structmember.h" - -#include - -/* Three symbols from graminit.h are also defined in Python.h, with - Py_ prefixes to their names. Python.h can't include graminit.h - (which defines too many confusing symbols), but we can check here - that they haven't changed (which is very unlikely, but possible). */ -#if Py_single_input != single_input - #error "single_input has changed -- update Py_single_input in Python.h" -#endif -#if Py_file_input != file_input - #error "file_input has changed -- update Py_file_input in Python.h" -#endif -#if Py_eval_input != eval_input - #error "eval_input has changed -- update Py_eval_input in Python.h" -#endif int Py_OptimizeFlag = 0; -#define OP_DELETE 0 -#define OP_ASSIGN 1 -#define OP_APPLY 2 +/* + ISSUES: -#define VAR_LOAD 0 -#define VAR_STORE 1 -#define VAR_DELETE 2 + character encodings aren't handled -#define DEL_CLOSURE_ERROR \ -"can not delete variable '%.400s' referenced in nested scope" + ref leaks in interpreter when press return on empty line -#define DUPLICATE_ARGUMENT \ -"duplicate argument '%s' in function definition" + opcode_stack_effect() function should be reviewed since stack depth bugs + could be really hard to find later. -#define GLOBAL_AFTER_ASSIGN \ -"name '%.400s' is assigned to before global declaration" - -#define GLOBAL_AFTER_USE \ -"name '%.400s' is used prior to global declaration" + Dead code is being generated (i.e. after unconditional jumps). +*/ -#define PARAM_GLOBAL \ -"name '%.400s' is a function parameter and declared global" +#define DEFAULT_BLOCK_SIZE 16 +#define DEFAULT_BLOCKS 8 +#define DEFAULT_CODE_SIZE 128 +#define DEFAULT_LNOTAB_SIZE 16 + +struct instr { + int i_jabs : 1; + int i_jrel : 1; + int i_hasarg : 1; + unsigned char i_opcode; + int i_oparg; + struct basicblock_ *i_target; /* target block (if jump instruction) */ + int i_lineno; +}; -#define LATE_FUTURE \ -"from __future__ imports must occur at the beginning of the file" +typedef struct basicblock_ { + /* next block in the list of blocks for a unit (don't confuse with + * b_next) */ + struct basicblock_ *b_list; + /* number of instructions used */ + int b_iused; + /* length of instruction array (b_instr) */ + int b_ialloc; + /* pointer to an array of instructions, initially NULL */ + struct instr *b_instr; + /* If b_next is non-NULL, it is a pointer to the next + block reached by normal control flow. */ + struct basicblock_ *b_next; + /* b_seen is used to perform a DFS of basicblocks. */ + int b_seen : 1; + /* b_return is true if a RETURN_VALUE opcode is inserted. */ + int b_return : 1; + /* depth of stack upon entry of block, computed by stackdepth() */ + int b_startdepth; + /* instruction offset for block, computed by assemble_jump_offsets() */ + int b_offset; +} basicblock; + +/* fblockinfo tracks the current frame block. + + A frame block is used to handle loops, try/except, and try/finally. + It's called a frame block to distinguish it from a basic block in the + compiler IR. +*/ -#define ASSIGN_DEBUG \ -"can not assign to __debug__" +enum fblocktype { LOOP, EXCEPT, FINALLY_TRY, FINALLY_END }; -#define MANGLE_LEN 256 +struct fblockinfo { + enum fblocktype fb_type; + basicblock *fb_block; +}; -#define OFF(x) offsetof(PyCodeObject, x) +/* The following items change on entry and exit of code blocks. + They must be saved and restored when returning to a block. +*/ +struct compiler_unit { + PySTEntryObject *u_ste; -static PyMemberDef code_memberlist[] = { - {"co_argcount", T_INT, OFF(co_argcount), READONLY}, - {"co_nlocals", T_INT, OFF(co_nlocals), READONLY}, - {"co_stacksize",T_INT, OFF(co_stacksize), READONLY}, - {"co_flags", T_INT, OFF(co_flags), READONLY}, - {"co_code", T_OBJECT, OFF(co_code), READONLY}, - {"co_consts", T_OBJECT, OFF(co_consts), READONLY}, - {"co_names", T_OBJECT, OFF(co_names), READONLY}, - {"co_varnames", T_OBJECT, OFF(co_varnames), READONLY}, - {"co_freevars", T_OBJECT, OFF(co_freevars), READONLY}, - {"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY}, - {"co_filename", T_OBJECT, OFF(co_filename), READONLY}, - {"co_name", T_OBJECT, OFF(co_name), READONLY}, - {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, - {"co_lnotab", T_OBJECT, OFF(co_lnotab), READONLY}, - {NULL} /* Sentinel */ + PyObject *u_name; + /* The following fields are dicts that map objects to + the index of them in co_XXX. The index is used as + the argument for opcodes that refer to those collections. + */ + PyObject *u_consts; /* all constants */ + PyObject *u_names; /* all names */ + PyObject *u_varnames; /* local variables */ + PyObject *u_cellvars; /* cell variables */ + PyObject *u_freevars; /* free variables */ + + PyObject *u_private; /* for private name mangling */ + + int u_argcount; /* number of arguments for block */ + basicblock *u_blocks; /* pointer to list of blocks */ + basicblock *u_curblock; /* pointer to current block */ + int u_tmpname; /* temporary variables for list comps */ + + int u_nfblocks; + struct fblockinfo u_fblock[CO_MAXBLOCKS]; + + int u_firstlineno; /* the first lineno of the block */ + int u_lineno; /* the lineno for the current stmt */ + bool u_lineno_set; /* boolean to indicate whether instr + has been generated with current lineno */ }; -/* Helper for code_new: return a shallow copy of a tuple that is - guaranteed to contain exact strings, by converting string subclasses - to exact strings and complaining if a non-string is found. */ -static PyObject* -validate_and_copy_tuple(PyObject *tup) -{ - PyObject *newtuple; - PyObject *item; - int i, len; +/* This struct captures the global state of a compilation. - len = PyTuple_GET_SIZE(tup); - newtuple = PyTuple_New(len); - if (newtuple == NULL) - return NULL; + The u pointer points to the current compilation unit, while units + for enclosing blocks are stored in c_stack. The u and c_stack are + managed by compiler_enter_scope() and compiler_exit_scope(). +*/ - for (i = 0; i < len; i++) { - item = PyTuple_GET_ITEM(tup, i); - if (PyString_CheckExact(item)) { - Py_INCREF(item); - } - else if (!PyString_Check(item)) { - PyErr_Format( - PyExc_TypeError, - "name tuples must contain only " - "strings, not '%.500s'", - item->ob_type->tp_name); - Py_DECREF(newtuple); - return NULL; - } - else { - item = PyString_FromStringAndSize( - PyString_AS_STRING(item), - PyString_GET_SIZE(item)); - if (item == NULL) { - Py_DECREF(newtuple); - return NULL; - } - } - PyTuple_SET_ITEM(newtuple, i, item); - } +struct compiler { + const char *c_filename; + struct symtable *c_st; + PyFutureFeatures *c_future; /* pointer to module's __future__ */ + PyCompilerFlags *c_flags; - return newtuple; -} + int c_interactive; + int c_nestlevel; -PyDoc_STRVAR(code_doc, -"code(argcount, nlocals, stacksize, flags, codestring, constants, names,\n\ - varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])\n\ -\n\ -Create a code object. Not for the faint of heart."); + struct compiler_unit *u; /* compiler state for current block */ + PyObject *c_stack; /* Python list holding compiler_unit ptrs */ + char *c_encoding; /* source encoding (a borrowed reference) */ +}; -static PyObject * -code_new(PyTypeObject *type, PyObject *args, PyObject *kw) +struct assembler { + PyObject *a_bytecode; /* string containing bytecode */ + int a_offset; /* offset into bytecode */ + int a_nblocks; /* number of reachable blocks */ + basicblock **a_postorder; /* list of blocks in dfs postorder */ + PyObject *a_lnotab; /* string containing lnotab */ + int a_lnotab_off; /* offset into lnotab */ + int a_lineno; /* last lineno of emitted instruction */ + int a_lineno_off; /* bytecode offset of last lineno */ +}; + +static int compiler_enter_scope(struct compiler *, identifier, void *, int); +static void compiler_free(struct compiler *); +static basicblock *compiler_new_block(struct compiler *); +static int compiler_next_instr(struct compiler *, basicblock *); +static int compiler_addop(struct compiler *, int); +static int compiler_addop_o(struct compiler *, int, PyObject *, PyObject *); +static int compiler_addop_i(struct compiler *, int, int); +static int compiler_addop_j(struct compiler *, int, basicblock *, int); +static void compiler_use_block(struct compiler *, basicblock *); +static basicblock *compiler_use_new_block(struct compiler *); +static int compiler_error(struct compiler *, const char *); +static int compiler_nameop(struct compiler *, identifier, expr_context_ty); + +static PyCodeObject *compiler_mod(struct compiler *, mod_ty); +static int compiler_visit_stmt(struct compiler *, stmt_ty); +static int compiler_visit_keyword(struct compiler *, keyword_ty); +static int compiler_visit_expr(struct compiler *, expr_ty); +static int compiler_augassign(struct compiler *, stmt_ty); +static int compiler_visit_slice(struct compiler *, slice_ty, + expr_context_ty); + +static int compiler_push_fblock(struct compiler *, enum fblocktype, + basicblock *); +static void compiler_pop_fblock(struct compiler *, enum fblocktype, + basicblock *); + +static int inplace_binop(struct compiler *, operator_ty); +static int expr_constant(expr_ty e); + +static PyCodeObject *assemble(struct compiler *, int addNone); +static PyObject *__doc__; + +PyObject * +_Py_Mangle(PyObject *private, PyObject *ident) { - int argcount; - int nlocals; - int stacksize; - int flags; - PyObject *co = NULL; - PyObject *code; - PyObject *consts; - PyObject *names, *ournames = NULL; - PyObject *varnames, *ourvarnames = NULL; - PyObject *freevars = NULL, *ourfreevars = NULL; - PyObject *cellvars = NULL, *ourcellvars = NULL; - PyObject *filename; - PyObject *name; - int firstlineno; - PyObject *lnotab; - - if (!PyArg_ParseTuple(args, "iiiiSO!O!O!SSiS|O!O!:code", - &argcount, &nlocals, &stacksize, &flags, - &code, - &PyTuple_Type, &consts, - &PyTuple_Type, &names, - &PyTuple_Type, &varnames, - &filename, &name, - &firstlineno, &lnotab, - &PyTuple_Type, &freevars, - &PyTuple_Type, &cellvars)) - return NULL; + /* Name mangling: __private becomes _classname__private. + This is independent from how the name is used. */ + const char *p, *name = PyString_AsString(ident); + char *buffer; + size_t nlen, plen; + if (private == NULL || name == NULL || name[0] != '_' || name[1] != '_') { + Py_INCREF(ident); + return ident; + } + p = PyString_AsString(private); + nlen = strlen(name); + if (name[nlen-1] == '_' && name[nlen-2] == '_') { + Py_INCREF(ident); + return ident; /* Don't mangle __whatever__ */ + } + /* Strip leading underscores from class name */ + while (*p == '_') + p++; + if (*p == '\0') { + Py_INCREF(ident); + return ident; /* Don't mangle if class is just underscores */ + } + plen = strlen(p); + ident = PyString_FromStringAndSize(NULL, 1 + nlen + plen); + if (!ident) + return 0; + /* ident = "_" + p[:plen] + name # i.e. 1+plen+nlen bytes */ + buffer = PyString_AS_STRING(ident); + buffer[0] = '_'; + strncpy(buffer+1, p, plen); + strcpy(buffer+1+plen, name); + return ident; +} - if (argcount < 0) { - PyErr_SetString( - PyExc_ValueError, - "code: argcount must not be negative"); - goto cleanup; - } +static int +compiler_init(struct compiler *c) +{ + memset(c, 0, sizeof(struct compiler)); + + c->c_stack = PyList_New(0); + if (!c->c_stack) + return 0; + + return 1; +} - if (nlocals < 0) { - PyErr_SetString( - PyExc_ValueError, - "code: nlocals must not be negative"); - goto cleanup; +PyCodeObject * +PyAST_Compile(mod_ty mod, const char *filename, PyCompilerFlags *flags) +{ + struct compiler c; + PyCodeObject *co = NULL; + PyCompilerFlags local_flags; + int merged; + + if (!__doc__) { + __doc__ = PyString_InternFromString("__doc__"); + if (!__doc__) + goto error; + } + + if (!compiler_init(&c)) + goto error; + c.c_filename = filename; + c.c_future = PyFuture_FromAST(mod, filename); + if (c.c_future == NULL) + goto error; + if (!flags) { + local_flags.cf_flags = 0; + flags = &local_flags; + } + merged = c.c_future->ff_features | flags->cf_flags; + c.c_future->ff_features = merged; + flags->cf_flags = merged; + c.c_flags = flags; + c.c_nestlevel = 0; + + c.c_st = PySymtable_Build(mod, filename, c.c_future); + if (c.c_st == NULL) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_SystemError, "no symtable"); + goto error; } - ournames = validate_and_copy_tuple(names); - if (ournames == NULL) - goto cleanup; - ourvarnames = validate_and_copy_tuple(varnames); - if (ourvarnames == NULL) - goto cleanup; - if (freevars) - ourfreevars = validate_and_copy_tuple(freevars); - else - ourfreevars = PyTuple_New(0); - if (ourfreevars == NULL) - goto cleanup; - if (cellvars) - ourcellvars = validate_and_copy_tuple(cellvars); - else - ourcellvars = PyTuple_New(0); - if (ourcellvars == NULL) - goto cleanup; - - co = (PyObject *) PyCode_New(argcount, nlocals, stacksize, flags, - code, consts, ournames, ourvarnames, - ourfreevars, ourcellvars, filename, - name, firstlineno, lnotab); - cleanup: - Py_XDECREF(ournames); - Py_XDECREF(ourvarnames); - Py_XDECREF(ourfreevars); - Py_XDECREF(ourcellvars); + /* XXX initialize to NULL for now, need to handle */ + c.c_encoding = NULL; + + co = compiler_mod(&c, mod); + + error: + compiler_free(&c); return co; } -static void -code_dealloc(PyCodeObject *co) +PyCodeObject * +PyNode_Compile(struct _node *n, const char *filename) { - Py_XDECREF(co->co_code); - Py_XDECREF(co->co_consts); - Py_XDECREF(co->co_names); - Py_XDECREF(co->co_varnames); - Py_XDECREF(co->co_freevars); - Py_XDECREF(co->co_cellvars); - Py_XDECREF(co->co_filename); - Py_XDECREF(co->co_name); - Py_XDECREF(co->co_lnotab); - PyObject_DEL(co); + PyCodeObject *co; + mod_ty mod = PyAST_FromNode(n, NULL, filename); + if (!mod) + return NULL; + co = PyAST_Compile(mod, filename, NULL); + free_mod(mod); + return co; } -static PyObject * -code_repr(PyCodeObject *co) +static void +compiler_free(struct compiler *c) { - char buf[500]; - int lineno = -1; - char *filename = "???"; - char *name = "???"; - - if (co->co_firstlineno != 0) - lineno = co->co_firstlineno; - if (co->co_filename && PyString_Check(co->co_filename)) - filename = PyString_AS_STRING(co->co_filename); - if (co->co_name && PyString_Check(co->co_name)) - name = PyString_AS_STRING(co->co_name); - PyOS_snprintf(buf, sizeof(buf), - "", - name, co, filename, lineno); - return PyString_FromString(buf); + if (c->c_st) + PySymtable_Free(c->c_st); + if (c->c_future) + PyObject_Free((void *)c->c_future); + Py_DECREF(c->c_stack); } -static int -code_compare(PyCodeObject *co, PyCodeObject *cp) +static PyObject * +list2dict(PyObject *list) { - int cmp; - cmp = PyObject_Compare(co->co_name, cp->co_name); - if (cmp) return cmp; - cmp = co->co_argcount - cp->co_argcount; - if (cmp) return (cmp<0)?-1:1; - cmp = co->co_nlocals - cp->co_nlocals; - if (cmp) return (cmp<0)?-1:1; - cmp = co->co_flags - cp->co_flags; - if (cmp) return (cmp<0)?-1:1; - cmp = co->co_firstlineno - cp->co_firstlineno; - if (cmp) return (cmp<0)?-1:1; - cmp = PyObject_Compare(co->co_code, cp->co_code); - if (cmp) return cmp; - cmp = PyObject_Compare(co->co_consts, cp->co_consts); - if (cmp) return cmp; - cmp = PyObject_Compare(co->co_names, cp->co_names); - if (cmp) return cmp; - cmp = PyObject_Compare(co->co_varnames, cp->co_varnames); - if (cmp) return cmp; - cmp = PyObject_Compare(co->co_freevars, cp->co_freevars); - if (cmp) return cmp; - cmp = PyObject_Compare(co->co_cellvars, cp->co_cellvars); - return cmp; -} + int i, n; + PyObject *v, *k, *dict = PyDict_New(); -static long -code_hash(PyCodeObject *co) -{ - long h, h0, h1, h2, h3, h4, h5, h6; - h0 = PyObject_Hash(co->co_name); - if (h0 == -1) return -1; - h1 = PyObject_Hash(co->co_code); - if (h1 == -1) return -1; - h2 = PyObject_Hash(co->co_consts); - if (h2 == -1) return -1; - h3 = PyObject_Hash(co->co_names); - if (h3 == -1) return -1; - h4 = PyObject_Hash(co->co_varnames); - if (h4 == -1) return -1; - h5 = PyObject_Hash(co->co_freevars); - if (h5 == -1) return -1; - h6 = PyObject_Hash(co->co_cellvars); - if (h6 == -1) return -1; - h = h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^ h6 ^ - co->co_argcount ^ co->co_nlocals ^ co->co_flags; - if (h == -1) h = -2; - return h; + n = PyList_Size(list); + for (i = 0; i < n; i++) { + v = PyInt_FromLong(i); + if (!v) { + Py_DECREF(dict); + return NULL; + } + k = PyList_GET_ITEM(list, i); + k = Py_BuildValue("(OO)", k, k->ob_type); + if (k == NULL || PyDict_SetItem(dict, k, v) < 0) { + Py_XDECREF(k); + Py_DECREF(v); + Py_DECREF(dict); + return NULL; + } + Py_DECREF(v); + } + return dict; } -/* XXX code objects need to participate in GC? */ - -PyTypeObject PyCode_Type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, - "code", - sizeof(PyCodeObject), - 0, - (destructor)code_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - (cmpfunc)code_compare, /* tp_compare */ - (reprfunc)code_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)code_hash, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - code_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - code_memberlist, /* tp_members */ - 0, /* 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 */ - code_new, /* tp_new */ -}; - -#define NAME_CHARS \ - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" +/* Return new dict containing names from src that match scope(s). -/* all_name_chars(s): true iff all chars in s are valid NAME_CHARS */ + src is a symbol table dictionary. If the scope of a name matches + either scope_type or flag is set, insert it into the new dict. The + values are integers, starting at offset and increasing by one for + each key. +*/ -static int -all_name_chars(unsigned char *s) +static PyObject * +dictbytype(PyObject *src, int scope_type, int flag, int offset) { - static char ok_name_char[256]; - static unsigned char *name_chars = (unsigned char *)NAME_CHARS; + int pos = 0, i = offset, scope; + PyObject *k, *v, *dest = PyDict_New(); - if (ok_name_char[*name_chars] == 0) { - unsigned char *p; - for (p = name_chars; *p; p++) - ok_name_char[*p] = 1; - } - while (*s) { - if (ok_name_char[*s++] == 0) - return 0; - } - return 1; -} + assert(offset >= 0); + if (dest == NULL) + return NULL; -static void -intern_strings(PyObject *tuple) -{ - int i; + while (PyDict_Next(src, &pos, &k, &v)) { + /* XXX this should probably be a macro in symtable.h */ + assert(PyInt_Check(v)); + scope = (PyInt_AS_LONG(v) >> SCOPE_OFF) & SCOPE_MASK; - for (i = PyTuple_GET_SIZE(tuple); --i >= 0; ) { - PyObject *v = PyTuple_GET_ITEM(tuple, i); - if (v == NULL || !PyString_CheckExact(v)) { - Py_FatalError("non-string found in code slot"); + if (scope == scope_type || PyInt_AS_LONG(v) & flag) { + PyObject *tuple, *item = PyInt_FromLong(i); + if (item == NULL) { + Py_DECREF(dest); + return NULL; + } + i++; + tuple = Py_BuildValue("(OO)", k, k->ob_type); + if (!tuple || PyDict_SetItem(dest, tuple, item) < 0) { + Py_DECREF(item); + Py_DECREF(dest); + Py_XDECREF(tuple); + return NULL; } - PyString_InternInPlace(&PyTuple_GET_ITEM(tuple, i)); + Py_DECREF(item); + Py_DECREF(tuple); + } } + return dest; } /* Begin: Peephole optimizations ----------------------------------------- */ @@ -974,5849 +968,3122 @@ exitUnchanged: /* End: Peephole optimizations ----------------------------------------- */ -PyCodeObject * -PyCode_New(int argcount, int nlocals, int stacksize, int flags, - PyObject *code, PyObject *consts, PyObject *names, - PyObject *varnames, PyObject *freevars, PyObject *cellvars, - PyObject *filename, PyObject *name, int firstlineno, - PyObject *lnotab) -{ - PyCodeObject *co; - int i; - /* Check argument types */ - if (argcount < 0 || nlocals < 0 || - code == NULL || - consts == NULL || !PyTuple_Check(consts) || - names == NULL || !PyTuple_Check(names) || - varnames == NULL || !PyTuple_Check(varnames) || - freevars == NULL || !PyTuple_Check(freevars) || - cellvars == NULL || !PyTuple_Check(cellvars) || - name == NULL || !PyString_Check(name) || - filename == NULL || !PyString_Check(filename) || - lnotab == NULL || !PyString_Check(lnotab) || - !PyObject_CheckReadBuffer(code)) { - PyErr_BadInternalCall(); - return NULL; - } - intern_strings(names); - intern_strings(varnames); - intern_strings(freevars); - intern_strings(cellvars); - /* Intern selected string constants */ - for (i = PyTuple_Size(consts); --i >= 0; ) { - PyObject *v = PyTuple_GetItem(consts, i); - if (!PyString_Check(v)) - continue; - if (!all_name_chars((unsigned char *)PyString_AS_STRING(v))) - continue; - PyString_InternInPlace(&PyTuple_GET_ITEM(consts, i)); - } - co = PyObject_NEW(PyCodeObject, &PyCode_Type); - if (co != NULL) { - co->co_argcount = argcount; - co->co_nlocals = nlocals; - co->co_stacksize = stacksize; - co->co_flags = flags; - Py_INCREF(code); - co->co_code = code; - Py_INCREF(consts); - co->co_consts = consts; - Py_INCREF(names); - co->co_names = names; - Py_INCREF(varnames); - co->co_varnames = varnames; - Py_INCREF(freevars); - co->co_freevars = freevars; - Py_INCREF(cellvars); - co->co_cellvars = cellvars; - Py_INCREF(filename); - co->co_filename = filename; - Py_INCREF(name); - co->co_name = name; - co->co_firstlineno = firstlineno; - Py_INCREF(lnotab); - co->co_lnotab = lnotab; - if (PyTuple_GET_SIZE(freevars) == 0 && - PyTuple_GET_SIZE(cellvars) == 0) - co->co_flags |= CO_NOFREE; +/* + +Leave this debugging code for just a little longer. + +static void +compiler_display_symbols(PyObject *name, PyObject *symbols) +{ + PyObject *key, *value; + int flags, pos = 0; + + fprintf(stderr, "block %s\n", PyString_AS_STRING(name)); + while (PyDict_Next(symbols, &pos, &key, &value)) { + flags = PyInt_AsLong(value); + fprintf(stderr, "var %s:", PyString_AS_STRING(key)); + if (flags & DEF_GLOBAL) + fprintf(stderr, " declared_global"); + if (flags & DEF_LOCAL) + fprintf(stderr, " local"); + if (flags & DEF_PARAM) + fprintf(stderr, " param"); + if (flags & DEF_STAR) + fprintf(stderr, " stararg"); + if (flags & DEF_DOUBLESTAR) + fprintf(stderr, " starstar"); + if (flags & DEF_INTUPLE) + fprintf(stderr, " tuple"); + if (flags & DEF_FREE) + fprintf(stderr, " free"); + if (flags & DEF_FREE_GLOBAL) + fprintf(stderr, " global"); + if (flags & DEF_FREE_CLASS) + fprintf(stderr, " free/class"); + if (flags & DEF_IMPORT) + fprintf(stderr, " import"); + fprintf(stderr, "\n"); } - return co; + fprintf(stderr, "\n"); } - - -/* Data structure used internally */ - -/* The compiler uses two passes to generate bytecodes. The first pass - builds the symbol table. The second pass generates the bytecode. - - The first pass uses a single symtable struct. The second pass uses - a compiling struct for each code block. The compiling structs - share a reference to the symtable. - - The two passes communicate via symtable_load_symbols() and via - is_local() and is_global(). The former initializes several slots - in the compiling struct: c_varnames, c_locals, c_nlocals, - c_argcount, c_globals, and c_flags. -*/ - -/* All about c_lnotab. - -c_lnotab is an array of unsigned bytes disguised as a Python string. Since -version 2.3, SET_LINENO opcodes are never generated and bytecode offsets are -mapped to source code line #s via c_lnotab instead. - -The array is conceptually a list of - (bytecode offset increment, line number increment) -pairs. The details are important and delicate, best illustrated by example: - - byte code offset source code line number - 0 1 - 6 2 - 50 7 - 350 307 - 361 308 - -The first trick is that these numbers aren't stored, only the increments -from one row to the next (this doesn't really work, but it's a start): - - 0, 1, 6, 1, 44, 5, 300, 300, 11, 1 - -The second trick is that an unsigned byte can't hold negative values, or -values larger than 255, so (a) there's a deep assumption that byte code -offsets and their corresponding line #s both increase monotonically, and (b) -if at least one column jumps by more than 255 from one row to the next, more -than one pair is written to the table. In case #b, there's no way to know -from looking at the table later how many were written. That's the delicate -part. A user of c_lnotab desiring to find the source line number -corresponding to a bytecode address A should do something like this - - lineno = addr = 0 - for addr_incr, line_incr in c_lnotab: - addr += addr_incr - if addr > A: - return lineno - lineno += line_incr - -In order for this to work, when the addr field increments by more than 255, -the line # increment in each pair generated must be 0 until the remaining addr -increment is < 256. So, in the example above, com_set_lineno should not (as -was actually done until 2.2) expand 300, 300 to 255, 255, 45, 45, but to -255, 0, 45, 255, 0, 45. */ -struct compiling { - PyObject *c_code; /* string */ - PyObject *c_consts; /* list of objects */ - PyObject *c_const_dict; /* inverse of c_consts */ - PyObject *c_names; /* list of strings (names) */ - PyObject *c_name_dict; /* inverse of c_names */ - PyObject *c_globals; /* dictionary (value=None or True) */ - PyObject *c_locals; /* dictionary (value=localID) */ - PyObject *c_varnames; /* list (inverse of c_locals) */ - PyObject *c_freevars; /* dictionary (value=None) */ - PyObject *c_cellvars; /* dictionary */ - int c_nlocals; /* index of next local */ - int c_argcount; /* number of top-level arguments */ - int c_flags; /* same as co_flags */ - int c_nexti; /* index into c_code */ - int c_errors; /* counts errors occurred */ - int c_infunction; /* set when compiling a function */ - int c_interactive; /* generating code for interactive command */ - int c_loops; /* counts nested loops */ - int c_begin; /* begin of current loop, for 'continue' */ - int c_block[CO_MAXBLOCKS]; /* stack of block types */ - int c_nblocks; /* current block stack level */ - const char *c_filename; /* filename of current node */ - char *c_name; /* name of object (e.g. function) */ - int c_lineno; /* Current line number */ - int c_stacklevel; /* Current stack level */ - int c_maxstacklevel; /* Maximum stack level */ - int c_firstlineno; - PyObject *c_lnotab; /* Table mapping address to line number */ - int c_last_addr; /* last op addr seen and recorded in lnotab */ - int c_last_line; /* last line seen and recorded in lnotab */ - int c_lnotab_next; /* current length of lnotab */ - int c_lnotab_last; /* start of last lnotab record added */ - char *c_private; /* for private name mangling */ - int c_tmpname; /* temporary local name counter */ - int c_nested; /* Is block nested funcdef or lamdef? */ - int c_closure; /* Is nested w/freevars? */ - struct symtable *c_symtable; /* pointer to module symbol table */ - PyFutureFeatures *c_future; /* pointer to module's __future__ */ - char *c_encoding; /* source encoding (a borrowed reference) */ -}; - -static int -is_free(int v) +static void +compiler_unit_check(struct compiler_unit *u) { - if ((v & (USE | DEF_FREE)) - && !(v & (DEF_LOCAL | DEF_PARAM | DEF_GLOBAL))) - return 1; - if (v & DEF_FREE_CLASS) - return 1; - return 0; + basicblock *block; + for (block = u->u_blocks; block != NULL; block = block->b_list) { + assert(block != (void *)0xcbcbcbcb); + assert(block != (void *)0xfbfbfbfb); + assert(block != (void *)0xdbdbdbdb); + if (block->b_instr != NULL) { + assert(block->b_ialloc > 0); + assert(block->b_iused > 0); + assert(block->b_ialloc >= block->b_iused); + } + else { + assert (block->b_iused == 0); + assert (block->b_ialloc == 0); + } + } } static void -com_error(struct compiling *c, PyObject *exc, char *msg) +compiler_unit_free(struct compiler_unit *u) { - PyObject *t = NULL, *v = NULL, *w = NULL, *line = NULL; - - if (c == NULL) { - /* Error occurred via symtable call to - is_constant_false */ - PyErr_SetString(exc, msg); - return; - } - c->c_errors++; - if (c->c_lineno < 1 || c->c_interactive) { - /* Unknown line number or interactive input */ - PyErr_SetString(exc, msg); - return; - } - v = PyString_FromString(msg); - if (v == NULL) - return; /* MemoryError, too bad */ + basicblock *b, *next; - line = PyErr_ProgramText(c->c_filename, c->c_lineno); - if (line == NULL) { - Py_INCREF(Py_None); - line = Py_None; + compiler_unit_check(u); + b = u->u_blocks; + while (b != NULL) { + if (b->b_instr) + PyObject_Free((void *)b->b_instr); + next = b->b_list; + PyObject_Free((void *)b); + b = next; } - if (exc == PyExc_SyntaxError) { - t = Py_BuildValue("(ziOO)", c->c_filename, c->c_lineno, - Py_None, line); - if (t == NULL) - goto exit; - w = PyTuple_Pack(2, v, t); - if (w == NULL) - goto exit; - PyErr_SetObject(exc, w); - } else { - /* Make sure additional exceptions are printed with - file and line, also. */ - PyErr_SetObject(exc, v); - PyErr_SyntaxLocation(c->c_filename, c->c_lineno); - } - exit: - Py_XDECREF(t); - Py_XDECREF(v); - Py_XDECREF(w); - Py_XDECREF(line); + Py_XDECREF(u->u_ste); + Py_XDECREF(u->u_name); + Py_XDECREF(u->u_consts); + Py_XDECREF(u->u_names); + Py_XDECREF(u->u_varnames); + Py_XDECREF(u->u_freevars); + Py_XDECREF(u->u_cellvars); + Py_XDECREF(u->u_private); + PyObject_Free(u); } -/* Interface to the block stack */ - -static void -block_push(struct compiling *c, int type) +static int +compiler_enter_scope(struct compiler *c, identifier name, void *key, + int lineno) { - if (c->c_nblocks >= CO_MAXBLOCKS) { - com_error(c, PyExc_SystemError, - "too many statically nested blocks"); + struct compiler_unit *u; + + u = PyObject_Malloc(sizeof(struct compiler_unit)); + memset(u, 0, sizeof(struct compiler_unit)); + u->u_argcount = 0; + u->u_ste = PySymtable_Lookup(c->c_st, key); + if (!u->u_ste) { + compiler_unit_free(u); + return 0; } - else { - c->c_block[c->c_nblocks++] = type; + Py_INCREF(name); + u->u_name = name; + u->u_varnames = list2dict(u->u_ste->ste_varnames); + u->u_cellvars = dictbytype(u->u_ste->ste_symbols, CELL, 0, 0); + u->u_freevars = dictbytype(u->u_ste->ste_symbols, FREE, DEF_FREE_CLASS, + PyDict_Size(u->u_cellvars)); + + u->u_blocks = NULL; + u->u_tmpname = 0; + u->u_nfblocks = 0; + u->u_firstlineno = lineno; + u->u_lineno = 0; + u->u_lineno_set = false; + u->u_consts = PyDict_New(); + if (!u->u_consts) { + compiler_unit_free(u); + return 0; + } + u->u_names = PyDict_New(); + if (!u->u_names) { + compiler_unit_free(u); + return 0; } -} -static void -block_pop(struct compiling *c, int type) -{ - if (c->c_nblocks > 0) - c->c_nblocks--; - if (c->c_block[c->c_nblocks] != type && c->c_errors == 0) { - com_error(c, PyExc_SystemError, "bad block pop"); + u->u_private = NULL; + + /* Push the old compiler_unit on the stack. */ + if (c->u) { + PyObject *wrapper = PyCObject_FromVoidPtr(c->u, NULL); + if (PyList_Append(c->c_stack, wrapper) < 0) { + compiler_unit_free(u); + return 0; + } + Py_DECREF(wrapper); + u->u_private = c->u->u_private; + Py_XINCREF(u->u_private); } -} + c->u = u; -/* Prototype forward declarations */ - -static int issue_warning(const char *, const char *, int); -static int com_init(struct compiling *, const char *); -static void com_free(struct compiling *); -static void com_push(struct compiling *, int); -static void com_pop(struct compiling *, int); -static void com_done(struct compiling *); -static void com_node(struct compiling *, node *); -static void com_factor(struct compiling *, node *); -static void com_addbyte(struct compiling *, int); -static void com_addint(struct compiling *, int); -static void com_addoparg(struct compiling *, int, int); -static void com_addfwref(struct compiling *, int, int *); -static void com_backpatch(struct compiling *, int); -static int com_add(struct compiling *, PyObject *, PyObject *, PyObject *); -static int com_addconst(struct compiling *, PyObject *); -static int com_addname(struct compiling *, PyObject *); -static void com_addopname(struct compiling *, int, node *); -static void com_test(struct compiling *c, node *n); -static void com_list(struct compiling *, node *, int); -static void com_list_iter(struct compiling *, node *, node *, char *); -static void com_gen_iter(struct compiling *, node *, node *); -static int com_argdefs(struct compiling *, node *); -static void com_assign(struct compiling *, node *, int, node *); -static void com_assign_name(struct compiling *, node *, int); -static int com_make_closure(struct compiling *c, PyCodeObject *co); - -static PyCodeObject *icompile(node *, struct compiling *); -static PyCodeObject *jcompile(node *, const char *, struct compiling *, - PyCompilerFlags *); -static PyObject *parsestrplus(struct compiling*, node *); -static PyObject *parsestr(struct compiling *, char *); -static node *get_rawdocstring(node *); - -static int get_ref_type(struct compiling *, char *); - -/* symtable operations */ -static int symtable_lookup(struct symtable *st, char *name); -static struct symtable *symtable_build(node *, PyFutureFeatures *, - const char *filename); -static int symtable_load_symbols(struct compiling *); -static struct symtable *symtable_init(void); -static void symtable_enter_scope(struct symtable *, char *, int, int); -static int symtable_exit_scope(struct symtable *); -static int symtable_add_def(struct symtable *, char *, int); -static int symtable_add_def_o(struct symtable *, PyObject *, PyObject *, int); - -static void symtable_node(struct symtable *, node *); -static void symtable_funcdef(struct symtable *, node *); -static void symtable_default_args(struct symtable *, node *); -static void symtable_params(struct symtable *, node *); -static void symtable_params_fplist(struct symtable *, node *n); -static void symtable_global(struct symtable *, node *); -static void symtable_import(struct symtable *, node *); -static void symtable_assign(struct symtable *, node *, int); -static void symtable_list_comprehension(struct symtable *, node *); -static void symtable_generator_expression(struct symtable *, node *); -static void symtable_list_for(struct symtable *, node *); -static void symtable_gen_for(struct symtable *, node *, int); -static void symtable_gen_iter(struct symtable *, node *); - -static int symtable_update_free_vars(struct symtable *); -static int symtable_undo_free(struct symtable *, PyObject *, PyObject *); -static int symtable_check_global(struct symtable *, PyObject *, PyObject *); - -/* helper */ -static void -do_pad(int pad) -{ - int i; - for (i = 0; i < pad; ++i) - fprintf(stderr, " "); -} + c->c_nestlevel++; + if (compiler_use_new_block(c) < 0) + return 0; -static void -dump(node *n, int pad, int depth) -{ - int i; - if (depth == 0) - return; - do_pad(pad); - fprintf(stderr, "%d: %s\n", TYPE(n), STR(n)); - if (depth > 0) - depth--; - for (i = 0; i < NCH(n); ++i) - dump(CHILD(n, i), pad + 1, depth); + return 1; } static int -com_init(struct compiling *c, const char *filename) -{ - memset((void *)c, '\0', sizeof(struct compiling)); - if ((c->c_code = PyString_FromStringAndSize((char *)NULL, - 1000)) == NULL) - goto fail; - if ((c->c_consts = PyList_New(0)) == NULL) - goto fail; - if ((c->c_const_dict = PyDict_New()) == NULL) - goto fail; - if ((c->c_names = PyList_New(0)) == NULL) - goto fail; - if ((c->c_name_dict = PyDict_New()) == NULL) - goto fail; - if ((c->c_locals = PyDict_New()) == NULL) - goto fail; - if ((c->c_lnotab = PyString_FromStringAndSize((char *)NULL, - 1000)) == NULL) - goto fail; - c->c_globals = NULL; - c->c_varnames = NULL; - c->c_freevars = NULL; - c->c_cellvars = NULL; - c->c_nlocals = 0; - c->c_argcount = 0; - c->c_flags = 0; - c->c_nexti = 0; - c->c_errors = 0; - c->c_infunction = 0; - c->c_interactive = 0; - c->c_loops = 0; - c->c_begin = 0; - c->c_nblocks = 0; - c->c_filename = filename; - c->c_name = "?"; - c->c_lineno = 0; - c->c_stacklevel = 0; - c->c_maxstacklevel = 0; - c->c_firstlineno = 0; - c->c_last_addr = 0; - c->c_last_line = 0; - c->c_lnotab_next = 0; - c->c_lnotab_last = 0; - c->c_tmpname = 0; - c->c_nested = 0; - c->c_closure = 0; - c->c_symtable = NULL; - return 1; - - fail: - com_free(c); - return 0; -} +compiler_exit_scope(struct compiler *c) +{ + int n; + PyObject *wrapper; + + c->c_nestlevel--; + compiler_unit_free(c->u); + /* Restore c->u to the parent unit. */ + n = PyList_GET_SIZE(c->c_stack) - 1; + if (n >= 0) { + wrapper = PyList_GET_ITEM(c->c_stack, n); + c->u = (struct compiler_unit *)PyCObject_AsVoidPtr(wrapper); + if (PySequence_DelItem(c->c_stack, n) < 0) + return 0; + compiler_unit_check(c->u); + } + else + c->u = NULL; -static void -com_free(struct compiling *c) -{ - Py_XDECREF(c->c_code); - Py_XDECREF(c->c_consts); - Py_XDECREF(c->c_const_dict); - Py_XDECREF(c->c_names); - Py_XDECREF(c->c_name_dict); - Py_XDECREF(c->c_globals); - Py_XDECREF(c->c_locals); - Py_XDECREF(c->c_varnames); - Py_XDECREF(c->c_freevars); - Py_XDECREF(c->c_cellvars); - Py_XDECREF(c->c_lnotab); - if (c->c_future) - PyObject_FREE((void *)c->c_future); + return 1; /* XXX void? */ } -static void -com_push(struct compiling *c, int n) -{ - c->c_stacklevel += n; - if (c->c_stacklevel > c->c_maxstacklevel) { - c->c_maxstacklevel = c->c_stacklevel; - /* - fprintf(stderr, "%s:%s:%d max stack nexti=%d level=%d n=%d\n", - c->c_filename, c->c_name, c->c_lineno, - c->c_nexti, c->c_stacklevel, n); - */ - } -} +/* Allocate a new block and return a pointer to it. + Returns NULL on error. +*/ -static void -com_pop(struct compiling *c, int n) +static basicblock * +compiler_new_block(struct compiler *c) { - if (c->c_stacklevel < n) - c->c_stacklevel = 0; - else - c->c_stacklevel -= n; + basicblock *b; + struct compiler_unit *u; + + u = c->u; + b = (basicblock *)PyObject_Malloc(sizeof(basicblock)); + if (b == NULL) + return NULL; + memset((void *)b, 0, sizeof(basicblock)); + assert (b->b_next == NULL); + b->b_list = u->u_blocks; + u->u_blocks = b; + return b; } static void -com_done(struct compiling *c) +compiler_use_block(struct compiler *c, basicblock *block) { - if (c->c_code != NULL) - _PyString_Resize(&c->c_code, c->c_nexti); - if (c->c_lnotab != NULL) - _PyString_Resize(&c->c_lnotab, c->c_lnotab_next); + assert (block != NULL); + c->u->u_curblock = block; } -static int -com_check_size(PyObject **s, int offset) +static basicblock * +compiler_use_new_block(struct compiler *c) { - int len = PyString_GET_SIZE(*s); - if (offset >= len) - return _PyString_Resize(s, len * 2); - return 0; + basicblock *block = compiler_new_block(c); + if (block == NULL) + return NULL; + c->u->u_curblock = block; + return block; } -static void -com_addbyte(struct compiling *c, int byte) +static basicblock * +compiler_next_block(struct compiler *c) { - /*fprintf(stderr, "%3d: %3d\n", c->c_nexti, byte);*/ - assert(byte >= 0 && byte <= 255); - assert(c->c_code != 0); - if (com_check_size(&c->c_code, c->c_nexti)) { - c->c_errors++; - return; - } - PyString_AS_STRING(c->c_code)[c->c_nexti++] = byte; + basicblock *block = compiler_new_block(c); + if (block == NULL) + return NULL; + c->u->u_curblock->b_next = block; + c->u->u_curblock = block; + return block; } -static void -com_addint(struct compiling *c, int x) +static basicblock * +compiler_use_next_block(struct compiler *c, basicblock *block) { - com_addbyte(c, x & 0xff); - com_addbyte(c, x >> 8); /* XXX x should be positive */ + assert(block != NULL); + c->u->u_curblock->b_next = block; + c->u->u_curblock = block; + return block; } -static void -com_add_lnotab(struct compiling *c, int addr, int line) -{ - char *p; - if (c->c_lnotab == NULL) - return; - if (com_check_size(&c->c_lnotab, c->c_lnotab_next + 2)) { - c->c_errors++; - return; - } - p = PyString_AS_STRING(c->c_lnotab) + c->c_lnotab_next; - *p++ = addr; - *p++ = line; - c->c_lnotab_next += 2; -} +/* Returns the offset of the next instruction in the current block's + b_instr array. Resizes the b_instr as necessary. + Returns -1 on failure. + */ -static void -com_set_lineno(struct compiling *c, int lineno) -{ - c->c_lineno = lineno; - if (c->c_firstlineno == 0) { - c->c_firstlineno = c->c_last_line = lineno; - } - else { - int incr_addr = c->c_nexti - c->c_last_addr; - int incr_line = lineno - c->c_last_line; - c->c_lnotab_last = c->c_lnotab_next; - while (incr_addr > 255) { - com_add_lnotab(c, 255, 0); - incr_addr -= 255; +static int +compiler_next_instr(struct compiler *c, basicblock *b) +{ + assert(b != NULL); + if (b->b_instr == NULL) { + b->b_instr = PyObject_Malloc(sizeof(struct instr) * + DEFAULT_BLOCK_SIZE); + if (b->b_instr == NULL) { + PyErr_NoMemory(); + return -1; } - while (incr_line > 255) { - com_add_lnotab(c, incr_addr, 255); - incr_line -=255; - incr_addr = 0; + b->b_ialloc = DEFAULT_BLOCK_SIZE; + memset((char *)b->b_instr, 0, + sizeof(struct instr) * DEFAULT_BLOCK_SIZE); + } + else if (b->b_iused == b->b_ialloc) { + size_t oldsize, newsize; + oldsize = b->b_ialloc * sizeof(struct instr); + newsize = oldsize << 1; + if (newsize == 0) { + PyErr_NoMemory(); + return -1; } - if (incr_addr > 0 || incr_line > 0) - com_add_lnotab(c, incr_addr, incr_line); - c->c_last_addr = c->c_nexti; - c->c_last_line = lineno; - } -} - -static void -com_strip_lnotab(struct compiling *c) -{ - /* strip the last lnotab entry if no opcode were emitted. - * This prevents a line number to be generated on a final - * pass, like in the following example: - * - * if a: - * print 5 - * else: - * pass - * - * Without the fix, a line trace event would be generated - * on the pass even if a is true (because of the implicit - * return). - */ - if (c->c_nexti == c->c_last_addr && c->c_lnotab_last > 0) { - c->c_lnotab_next = c->c_lnotab_last; + b->b_ialloc <<= 1; + b->b_instr = PyObject_Realloc((void *)b->b_instr, newsize); + if (b->b_instr == NULL) + return -1; + memset((char *)b->b_instr + oldsize, 0, newsize - oldsize); } + return b->b_iused++; } static void -com_addoparg(struct compiling *c, int op, int arg) +compiler_set_lineno(struct compiler *c, int off) { - int extended_arg = arg >> 16; - if (extended_arg){ - com_addbyte(c, EXTENDED_ARG); - com_addint(c, extended_arg); - arg &= 0xffff; - } - com_addbyte(c, op); - com_addint(c, arg); + basicblock *b; + if (c->u->u_lineno_set) + return; + c->u->u_lineno_set = true; + b = c->u->u_curblock; + b->b_instr[off].i_lineno = c->u->u_lineno; } -static void -com_addfwref(struct compiling *c, int op, int *p_anchor) +static int +opcode_stack_effect(int opcode, int oparg) { - /* Compile a forward reference for backpatching */ - int here; - int anchor; - com_addbyte(c, op); - here = c->c_nexti; - anchor = *p_anchor; - *p_anchor = here; - com_addint(c, anchor == 0 ? 0 : here - anchor); -} + switch (opcode) { + case POP_TOP: + return -1; + case ROT_TWO: + case ROT_THREE: + return 0; + case DUP_TOP: + return 1; + case ROT_FOUR: + return 0; -static void -com_backpatch(struct compiling *c, int anchor) -{ - unsigned char *code = (unsigned char *) PyString_AS_STRING(c->c_code); - int target = c->c_nexti; - int dist; - int prev; - for (;;) { - /* Make the JUMP instruction at anchor point to target */ - prev = code[anchor] + (code[anchor+1] << 8); - dist = target - (anchor+2); - code[anchor] = dist & 0xff; - dist >>= 8; - code[anchor+1] = dist; - dist >>= 8; - if (dist) { - com_error(c, PyExc_SystemError, - "com_backpatch: offset too large"); - break; - } - if (!prev) - break; - anchor -= prev; - } -} - -/* Handle literals and names uniformly */ - -static int -com_add(struct compiling *c, PyObject *list, PyObject *dict, PyObject *v) -{ - PyObject *w, *t, *np=NULL; - long n; - - t = PyTuple_Pack(2, v, v->ob_type); - if (t == NULL) - goto fail; - w = PyDict_GetItem(dict, t); - if (w != NULL) { - n = PyInt_AsLong(w); - } else { - n = PyList_Size(list); - np = PyInt_FromLong(n); - if (np == NULL) - goto fail; - if (PyList_Append(list, v) != 0) - goto fail; - if (PyDict_SetItem(dict, t, np) != 0) - goto fail; - Py_DECREF(np); - } - Py_DECREF(t); - return n; - fail: - Py_XDECREF(np); - Py_XDECREF(t); - c->c_errors++; - return 0; -} - -static int -com_addconst(struct compiling *c, PyObject *v) -{ - return com_add(c, c->c_consts, c->c_const_dict, v); -} - -static int -com_addname(struct compiling *c, PyObject *v) -{ - return com_add(c, c->c_names, c->c_name_dict, v); -} - -int -_Py_Mangle(char *p, char *name, char *buffer, size_t maxlen) -{ - /* Name mangling: __private becomes _classname__private. - This is independent from how the name is used. */ - size_t nlen, plen; - if (p == NULL || name == NULL || name[0] != '_' || name[1] != '_') - return 0; - nlen = strlen(name); - if (nlen+2 >= maxlen) - return 0; /* Don't mangle __extremely_long_names */ - if (name[nlen-1] == '_' && name[nlen-2] == '_') - return 0; /* Don't mangle __whatever__ */ - /* Strip leading underscores from class name */ - while (*p == '_') - p++; - if (*p == '\0') - return 0; /* Don't mangle if class is just underscores */ - plen = strlen(p); - if (plen + nlen >= maxlen) - plen = maxlen-nlen-2; /* Truncate class name if too long */ - /* buffer = "_" + p[:plen] + name # i.e. 1+plen+nlen bytes */ - buffer[0] = '_'; - strncpy(buffer+1, p, plen); - strcpy(buffer+1+plen, name); - return 1; -} - -static void -com_addop_name(struct compiling *c, int op, char *name) -{ - PyObject *v; - int i; - char buffer[MANGLE_LEN]; - - if (_Py_Mangle(c->c_private, name, buffer, sizeof(buffer))) - name = buffer; - if (name == NULL || (v = PyString_InternFromString(name)) == NULL) { - c->c_errors++; - i = 255; - } - else { - i = com_addname(c, v); - Py_DECREF(v); - } - com_addoparg(c, op, i); -} - -#define NAME_LOCAL 0 -#define NAME_GLOBAL 1 -#define NAME_DEFAULT 2 -#define NAME_CLOSURE 3 - -static int -com_lookup_arg(PyObject *dict, PyObject *name) -{ - PyObject *v = PyDict_GetItem(dict, name); - if (v == NULL) - return -1; - else - return PyInt_AS_LONG(v); -} - -static int -none_assignment_check(struct compiling *c, char *name, int assigning) -{ - if (name[0] == 'N' && strcmp(name, "None") == 0) { - char *msg; - if (assigning) - msg = "assignment to None"; - else - msg = "deleting None"; - com_error(c, PyExc_SyntaxError, msg); - return -1; - } - return 0; -} - -static void -com_addop_varname(struct compiling *c, int kind, char *name) -{ - PyObject *v; - int i, reftype; - int scope = NAME_DEFAULT; - int op = STOP_CODE; - char buffer[MANGLE_LEN]; - - if (kind != VAR_LOAD && - none_assignment_check(c, name, kind == VAR_STORE)) - { - i = 255; - goto done; - } - if (_Py_Mangle(c->c_private, name, buffer, sizeof(buffer))) - name = buffer; - if (name == NULL || (v = PyString_InternFromString(name)) == NULL) { - c->c_errors++; - i = 255; - goto done; - } - - reftype = get_ref_type(c, name); - switch (reftype) { - case LOCAL: - if (c->c_symtable->st_cur->ste_type == TYPE_FUNCTION) - scope = NAME_LOCAL; - break; - case GLOBAL_EXPLICIT: - scope = NAME_GLOBAL; - break; - case GLOBAL_IMPLICIT: - if (c->c_flags & CO_OPTIMIZED) - scope = NAME_GLOBAL; - break; - case FREE: - case CELL: - scope = NAME_CLOSURE; - break; - } - - i = com_addname(c, v); - if (scope == NAME_LOCAL) - i = com_lookup_arg(c->c_locals, v); - else if (reftype == FREE) - i = com_lookup_arg(c->c_freevars, v); - else if (reftype == CELL) - i = com_lookup_arg(c->c_cellvars, v); - if (i == -1) { - c->c_errors++; /* XXX no exception set */ - i = 255; - goto done; - } - Py_DECREF(v); - - switch (kind) { - case VAR_LOAD: - switch (scope) { - case NAME_LOCAL: - op = LOAD_FAST; - break; - case NAME_GLOBAL: - op = LOAD_GLOBAL; - break; - case NAME_DEFAULT: - op = LOAD_NAME; - break; - case NAME_CLOSURE: - op = LOAD_DEREF; - break; - } - break; - case VAR_STORE: - switch (scope) { - case NAME_LOCAL: - op = STORE_FAST; - break; - case NAME_GLOBAL: - op = STORE_GLOBAL; - break; - case NAME_DEFAULT: - op = STORE_NAME; - break; - case NAME_CLOSURE: - op = STORE_DEREF; - break; - } - break; - case VAR_DELETE: - switch (scope) { - case NAME_LOCAL: - op = DELETE_FAST; - break; - case NAME_GLOBAL: - op = DELETE_GLOBAL; - break; - case NAME_DEFAULT: - op = DELETE_NAME; - break; - case NAME_CLOSURE: { - char buf[500]; - PyOS_snprintf(buf, sizeof(buf), - DEL_CLOSURE_ERROR, name); - com_error(c, PyExc_SyntaxError, buf); - i = 255; - break; - } - } - break; - } -done: - com_addoparg(c, op, i); -} - -static void -com_addopname(struct compiling *c, int op, node *n) -{ - char *name; - char buffer[1000]; - /* XXX it is possible to write this code without the 1000 - chars on the total length of dotted names, I just can't be - bothered right now */ - if (TYPE(n) == STAR) - name = "*"; - else if (TYPE(n) == dotted_name) { - char *p = buffer; - int i; - name = buffer; - for (i = 0; i < NCH(n); i += 2) { - char *s = STR(CHILD(n, i)); - if (p + strlen(s) > buffer + (sizeof buffer) - 2) { - com_error(c, PyExc_MemoryError, - "dotted_name too long"); - name = NULL; - break; - } - if (p != buffer) - *p++ = '.'; - strcpy(p, s); - p = strchr(p, '\0'); - } - } - else { - REQ(n, NAME); - name = STR(n); - } - com_addop_name(c, op, name); -} - -static PyObject * -parsenumber(struct compiling *c, char *s) -{ - char *end; - long x; - double dx; -#ifndef WITHOUT_COMPLEX - int imflag; -#endif - - errno = 0; - end = s + strlen(s) - 1; -#ifndef WITHOUT_COMPLEX - imflag = *end == 'j' || *end == 'J'; -#endif - if (*end == 'l' || *end == 'L') - return PyLong_FromString(s, (char **)0, 0); - if (s[0] == '0') { - x = (long) PyOS_strtoul(s, &end, 0); - if (x < 0 && errno == 0) { - return PyLong_FromString(s, (char **)0, 0); - } - } - else - x = PyOS_strtol(s, &end, 0); - if (*end == '\0') { - if (errno != 0) - return PyLong_FromString(s, (char **)0, 0); - return PyInt_FromLong(x); - } - /* XXX Huge floats may silently fail */ -#ifndef WITHOUT_COMPLEX - if (imflag) { - Py_complex z; - z.real = 0.; - PyFPE_START_PROTECT("atof", return 0) - z.imag = PyOS_ascii_atof(s); - PyFPE_END_PROTECT(z) - return PyComplex_FromCComplex(z); - } - else -#endif - { - PyFPE_START_PROTECT("atof", return 0) - dx = PyOS_ascii_atof(s); - PyFPE_END_PROTECT(dx) - return PyFloat_FromDouble(dx); - } -} - -static PyObject * -decode_utf8(char **sPtr, char *end, char* encoding) -{ -#ifndef Py_USING_UNICODE - Py_FatalError("decode_utf8 should not be called in this build."); - return NULL; -#else - PyObject *u, *v; - char *s, *t; - t = s = *sPtr; - /* while (s < end && *s != '\\') s++; */ /* inefficient for u".." */ - while (s < end && (*s & 0x80)) s++; - *sPtr = s; - u = PyUnicode_DecodeUTF8(t, s - t, NULL); - if (u == NULL) - return NULL; - v = PyUnicode_AsEncodedString(u, encoding, NULL); - Py_DECREF(u); - return v; -#endif -} - -/* compiler.transformer.Transformer.decode_literal depends on what - might seem like minor details of this function -- changes here - must be reflected there. */ -static PyObject * -parsestr(struct compiling *c, char *s) -{ - PyObject *v; - size_t len; - int quote = *s; - int rawmode = 0; - char* encoding = ((c == NULL) ? NULL : c->c_encoding); - int need_encoding; - int unicode = 0; - - if (isalpha(quote) || quote == '_') { - if (quote == 'u' || quote == 'U') { - quote = *++s; - unicode = 1; - } - if (quote == 'r' || quote == 'R') { - quote = *++s; - rawmode = 1; - } - } - if (quote != '\'' && quote != '\"') { - PyErr_BadInternalCall(); - return NULL; - } - s++; - len = strlen(s); - if (len > INT_MAX) { - com_error(c, PyExc_OverflowError, - "string to parse is too long"); - return NULL; - } - if (s[--len] != quote) { - PyErr_BadInternalCall(); - return NULL; - } - if (len >= 4 && s[0] == quote && s[1] == quote) { - s += 2; - len -= 2; - if (s[--len] != quote || s[--len] != quote) { - PyErr_BadInternalCall(); - return NULL; - } - } -#ifdef Py_USING_UNICODE - if (unicode || Py_UnicodeFlag) { - PyObject *u, *w; - char *buf; - char *p; - char *end; - if (encoding == NULL) { - buf = s; - u = NULL; - } else if (strcmp(encoding, "iso-8859-1") == 0) { - buf = s; - u = NULL; - } else { - /* "\XX" may become "\u005c\uHHLL" (12 bytes) */ - u = PyString_FromStringAndSize((char *)NULL, len * 4); - if (u == NULL) - return NULL; - p = buf = PyString_AsString(u); - end = s + len; - while (s < end) { - if (*s == '\\') { - *p++ = *s++; - if (*s & 0x80) { - strcpy(p, "u005c"); - p += 5; - } - } - if (*s & 0x80) { /* XXX inefficient */ - char *r; - int rn, i; - w = decode_utf8(&s, end, "utf-16-be"); - if (w == NULL) { - Py_DECREF(u); - return NULL; - } - r = PyString_AsString(w); - rn = PyString_Size(w); - assert(rn % 2 == 0); - for (i = 0; i < rn; i += 2) { - sprintf(p, "\\u%02x%02x", - r[i + 0] & 0xFF, - r[i + 1] & 0xFF); - p += 6; - } - Py_DECREF(w); - } else { - *p++ = *s++; - } - } - len = p - buf; - } - if (rawmode) - v = PyUnicode_DecodeRawUnicodeEscape(buf, len, NULL); - else - v = PyUnicode_DecodeUnicodeEscape(buf, len, NULL); - Py_XDECREF(u); - if (v == NULL) - PyErr_SyntaxLocation(c->c_filename, c->c_lineno); - return v; - - } -#endif - need_encoding = (encoding != NULL && - strcmp(encoding, "utf-8") != 0 && - strcmp(encoding, "iso-8859-1") != 0); - if (rawmode || strchr(s, '\\') == NULL) { - if (need_encoding) { -#ifndef Py_USING_UNICODE - /* This should not happen - we never see any other - encoding. */ - Py_FatalError("cannot deal with encodings in this build."); -#else - PyObject* u = PyUnicode_DecodeUTF8(s, len, NULL); - if (u == NULL) - return NULL; - v = PyUnicode_AsEncodedString(u, encoding, NULL); - Py_DECREF(u); - return v; -#endif - } else { - return PyString_FromStringAndSize(s, len); - } - } - - v = PyString_DecodeEscape(s, len, NULL, unicode, - need_encoding ? encoding : NULL); - if (v == NULL) - PyErr_SyntaxLocation(c->c_filename, c->c_lineno); - return v; -} - -static PyObject * -parsestrplus(struct compiling* c, node *n) -{ - PyObject *v; - int i; - REQ(CHILD(n, 0), STRING); - if ((v = parsestr(c, STR(CHILD(n, 0)))) != NULL) { - /* String literal concatenation */ - for (i = 1; i < NCH(n); i++) { - PyObject *s; - s = parsestr(c, STR(CHILD(n, i))); - if (s == NULL) - goto onError; - if (PyString_Check(v) && PyString_Check(s)) { - PyString_ConcatAndDel(&v, s); - if (v == NULL) - goto onError; - } -#ifdef Py_USING_UNICODE - else { - PyObject *temp; - temp = PyUnicode_Concat(v, s); - Py_DECREF(s); - if (temp == NULL) - goto onError; - Py_DECREF(v); - v = temp; - } -#endif - } - } - return v; - - onError: - Py_XDECREF(v); - return NULL; -} - -static void -com_list_for(struct compiling *c, node *n, node *e, char *t) -{ - int anchor = 0; - int save_begin = c->c_begin; - - /* list_for: for v in expr [list_iter] */ - com_node(c, CHILD(n, 3)); /* expr */ - com_addbyte(c, GET_ITER); - c->c_begin = c->c_nexti; - com_addfwref(c, FOR_ITER, &anchor); - com_push(c, 1); - com_assign(c, CHILD(n, 1), OP_ASSIGN, NULL); - c->c_loops++; - com_list_iter(c, n, e, t); - c->c_loops--; - com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); - c->c_begin = save_begin; - com_backpatch(c, anchor); - com_pop(c, 1); /* FOR_ITER has popped this */ -} - -static void -com_gen_for(struct compiling *c, node *n, node *t, int is_outmost) -{ - int break_anchor = 0; - int anchor = 0; - int save_begin = c->c_begin; - - REQ(n, gen_for); - /* gen_for: for v in test [gen_iter] */ - - com_addfwref(c, SETUP_LOOP, &break_anchor); - block_push(c, SETUP_LOOP); - - if (is_outmost) { - com_addop_varname(c, VAR_LOAD, "[outmost-iterable]"); - com_push(c, 1); - } - else { - com_node(c, CHILD(n, 3)); - com_addbyte(c, GET_ITER); - } - - c->c_begin = c->c_nexti; - com_set_lineno(c, c->c_last_line); - com_addfwref(c, FOR_ITER, &anchor); - com_push(c, 1); - com_assign(c, CHILD(n, 1), OP_ASSIGN, NULL); - - if (NCH(n) == 5) - com_gen_iter(c, CHILD(n, 4), t); - else { - com_test(c, t); - com_addbyte(c, YIELD_VALUE); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - } - - com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); - c->c_begin = save_begin; - - com_backpatch(c, anchor); - com_pop(c, 1); /* FOR_ITER has popped this */ - com_addbyte(c, POP_BLOCK); - block_pop(c, SETUP_LOOP); - com_backpatch(c, break_anchor); -} - -static void -com_list_if(struct compiling *c, node *n, node *e, char *t) -{ - int anchor = 0; - int a = 0; - /* list_iter: 'if' test [list_iter] */ - com_node(c, CHILD(n, 1)); - com_addfwref(c, JUMP_IF_FALSE, &a); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - com_list_iter(c, n, e, t); - com_addfwref(c, JUMP_FORWARD, &anchor); - com_backpatch(c, a); - /* We jump here with an extra entry which we now pop */ - com_addbyte(c, POP_TOP); - com_backpatch(c, anchor); -} - -static void -com_gen_if(struct compiling *c, node *n, node *t) -{ - /* gen_if: 'if' test [gen_iter] */ - int anchor = 0; - int a=0; - - com_node(c, CHILD(n, 1)); - com_addfwref(c, JUMP_IF_FALSE, &a); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - - if (NCH(n) == 3) - com_gen_iter(c, CHILD(n, 2), t); - else { - com_test(c, t); - com_addbyte(c, YIELD_VALUE); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - } - com_addfwref(c, JUMP_FORWARD, &anchor); - com_backpatch(c, a); - /* We jump here with an extra entry which we now pop */ - com_addbyte(c, POP_TOP); - com_backpatch(c, anchor); -} - -static void -com_list_iter(struct compiling *c, - node *p, /* parent of list_iter node */ - node *e, /* element expression node */ - char *t /* name of result list temp local */) -{ - /* list_iter is the last child in a listmaker, list_for, or list_if */ - node *n = CHILD(p, NCH(p)-1); - if (TYPE(n) == list_iter) { - n = CHILD(n, 0); - switch (TYPE(n)) { - case list_for: - com_list_for(c, n, e, t); - break; - case list_if: - com_list_if(c, n, e, t); - break; - default: - com_error(c, PyExc_SystemError, - "invalid list_iter node type"); - } - } - else { - com_addop_varname(c, VAR_LOAD, t); - com_push(c, 1); - com_node(c, e); - com_addbyte(c, LIST_APPEND); - com_pop(c, 2); - } -} - -static void -com_gen_iter(struct compiling *c, node *n, node *t) -{ - /* gen_iter: gen_for | gen_if */ - node *ch; - REQ(n, gen_iter); - - ch = CHILD(n, 0); - - switch (TYPE(ch)) { - case gen_for: - com_gen_for(c, ch, t, 0); - break; - case gen_if: - com_gen_if(c, ch, t); - break; - default: - com_error(c, PyExc_SystemError, - "invalid gen_iter node type"); - } -} - -static void -com_list_comprehension(struct compiling *c, node *n) -{ - /* listmaker: test list_for */ - char tmpname[30]; - - REQ(n, listmaker); - PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", ++c->c_tmpname); - com_addoparg(c, BUILD_LIST, 0); - com_addbyte(c, DUP_TOP); /* leave the result on the stack */ - com_push(c, 2); - com_addop_varname(c, VAR_STORE, tmpname); - com_pop(c, 1); - com_list_for(c, CHILD(n, 1), CHILD(n, 0), tmpname); - com_addop_varname(c, VAR_DELETE, tmpname); - --c->c_tmpname; -} - -static void -com_listmaker(struct compiling *c, node *n) -{ - /* listmaker: test ( list_for | (',' test)* [','] ) */ - if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) - com_list_comprehension(c, n); - else { - int len = 0; - int i; - for (i = 0; i < NCH(n); i += 2, len++) - com_node(c, CHILD(n, i)); - com_addoparg(c, BUILD_LIST, len); - com_pop(c, len-1); - } -} - -static void -com_generator_expression(struct compiling *c, node *n) -{ - /* testlist_gexp: test gen_for */ - /* argument: test gen_for */ - PyCodeObject *co; - - REQ(CHILD(n, 0), test); - REQ(CHILD(n, 1), gen_for); - - symtable_enter_scope(c->c_symtable, "", TYPE(n), - n->n_lineno); - co = icompile(n, c); - symtable_exit_scope(c->c_symtable); - - if (co == NULL) - c->c_errors++; - else { - int closure = com_make_closure(c, co); - int i = com_addconst(c, (PyObject *)co); - - com_addoparg(c, LOAD_CONST, i); - com_push(c, 1); - if (closure) - com_addoparg(c, MAKE_CLOSURE, 0); - else - com_addoparg(c, MAKE_FUNCTION, 0); - - com_test(c, CHILD(CHILD(n, 1), 3)); - com_addbyte(c, GET_ITER); - com_addoparg(c, CALL_FUNCTION, 1); - com_pop(c, 1); - - Py_DECREF(co); - } -} - -static void -com_testlist_gexp(struct compiling *c, node *n) -{ - /* testlist_gexp: test ( gen_for | (',' test)* [','] ) */ - if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == gen_for) - com_generator_expression(c, n); - else com_list(c, n, 0); -} - - -static void -com_dictmaker(struct compiling *c, node *n) -{ - int i; - /* dictmaker: test ':' test (',' test ':' value)* [','] */ - for (i = 0; i+2 < NCH(n); i += 4) { - /* We must arrange things just right for STORE_SUBSCR. - It wants the stack to look like (value) (dict) (key) */ - com_addbyte(c, DUP_TOP); - com_push(c, 1); - com_node(c, CHILD(n, i)); /* key */ - com_node(c, CHILD(n, i+2)); /* value */ - com_addbyte(c, ROT_THREE); - com_addbyte(c, STORE_SUBSCR); - com_pop(c, 3); - } -} - - -/* forward reference */ -static void com_yield_expr(struct compiling *c, node *n); - -static void -com_atom(struct compiling *c, node *n) -{ - node *ch; - PyObject *v; - int i; - REQ(n, atom); - ch = CHILD(n, 0); - switch (TYPE(ch)) { - case LPAR: - if (TYPE(CHILD(n, 1)) == RPAR) { - com_addoparg(c, BUILD_TUPLE, 0); - com_push(c, 1); - } - else - if (TYPE(CHILD(n, 1)) == yield_expr) - com_yield_expr(c, CHILD(n, 1)); - else - com_testlist_gexp(c, CHILD(n, 1)); - break; - case LSQB: /* '[' [listmaker] ']' */ - if (TYPE(CHILD(n, 1)) == RSQB) { - com_addoparg(c, BUILD_LIST, 0); - com_push(c, 1); - } - else - com_listmaker(c, CHILD(n, 1)); - break; - case LBRACE: /* '{' [dictmaker] '}' */ - com_addoparg(c, BUILD_MAP, 0); - com_push(c, 1); - if (TYPE(CHILD(n, 1)) == dictmaker) - com_dictmaker(c, CHILD(n, 1)); - break; - case BACKQUOTE: - com_node(c, CHILD(n, 1)); - com_addbyte(c, UNARY_CONVERT); - break; - case NUMBER: - if ((v = parsenumber(c, STR(ch))) == NULL) { - i = 255; - } - else { - i = com_addconst(c, v); - Py_DECREF(v); - } - com_addoparg(c, LOAD_CONST, i); - com_push(c, 1); - break; - case STRING: - v = parsestrplus(c, n); - if (v == NULL) { - c->c_errors++; - i = 255; - } - else { - i = com_addconst(c, v); - Py_DECREF(v); - } - com_addoparg(c, LOAD_CONST, i); - com_push(c, 1); - break; - case NAME: - com_addop_varname(c, VAR_LOAD, STR(ch)); - com_push(c, 1); - break; - default: - com_error(c, PyExc_SystemError, - "com_atom: unexpected node type"); - } -} - -static void -com_slice(struct compiling *c, node *n, int op) -{ - if (NCH(n) == 1) { - com_addbyte(c, op); - } - else if (NCH(n) == 2) { - if (TYPE(CHILD(n, 0)) != COLON) { - com_node(c, CHILD(n, 0)); - com_addbyte(c, op+1); - } - else { - com_node(c, CHILD(n, 1)); - com_addbyte(c, op+2); - } - com_pop(c, 1); - } - else { - com_node(c, CHILD(n, 0)); - com_node(c, CHILD(n, 2)); - com_addbyte(c, op+3); - com_pop(c, 2); - } -} - -static void -com_augassign_slice(struct compiling *c, node *n, int opcode, node *augn) -{ - if (NCH(n) == 1) { - com_addbyte(c, DUP_TOP); - com_push(c, 1); - com_addbyte(c, SLICE); - com_node(c, augn); - com_addbyte(c, opcode); - com_pop(c, 1); - com_addbyte(c, ROT_TWO); - com_addbyte(c, STORE_SLICE); - com_pop(c, 2); - } else if (NCH(n) == 2 && TYPE(CHILD(n, 0)) != COLON) { - com_node(c, CHILD(n, 0)); - com_addoparg(c, DUP_TOPX, 2); - com_push(c, 2); - com_addbyte(c, SLICE+1); - com_pop(c, 1); - com_node(c, augn); - com_addbyte(c, opcode); - com_pop(c, 1); - com_addbyte(c, ROT_THREE); - com_addbyte(c, STORE_SLICE+1); - com_pop(c, 3); - } else if (NCH(n) == 2) { - com_node(c, CHILD(n, 1)); - com_addoparg(c, DUP_TOPX, 2); - com_push(c, 2); - com_addbyte(c, SLICE+2); - com_pop(c, 1); - com_node(c, augn); - com_addbyte(c, opcode); - com_pop(c, 1); - com_addbyte(c, ROT_THREE); - com_addbyte(c, STORE_SLICE+2); - com_pop(c, 3); - } else { - com_node(c, CHILD(n, 0)); - com_node(c, CHILD(n, 2)); - com_addoparg(c, DUP_TOPX, 3); - com_push(c, 3); - com_addbyte(c, SLICE+3); - com_pop(c, 2); - com_node(c, augn); - com_addbyte(c, opcode); - com_pop(c, 1); - com_addbyte(c, ROT_FOUR); - com_addbyte(c, STORE_SLICE+3); - com_pop(c, 4); - } -} - -static void -com_argument(struct compiling *c, node *n, PyObject **pkeywords) -{ - node *m; - REQ(n, argument); /* [test '='] test [gen_for]; really [keyword '='] test */ - if (NCH(n) == 1) { - if (*pkeywords != NULL) { - com_error(c, PyExc_SyntaxError, - "non-keyword arg after keyword arg"); - } - else { - com_node(c, CHILD(n, 0)); - } - return; - } - if (NCH(n) == 2) { - com_generator_expression(c, n); - return; - } - - m = n; - do { - m = CHILD(m, 0); - } while (NCH(m) == 1); - if (TYPE(m) != NAME) { - /* 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. - */ - com_error(c, PyExc_SyntaxError, - TYPE(m) == lambdef ? - "lambda cannot contain assignment" : - "keyword can't be an expression"); - } - else { - PyObject *v = PyString_InternFromString(STR(m)); - (void) none_assignment_check(c, STR(m), 1); - if (v != NULL && *pkeywords == NULL) - *pkeywords = PyDict_New(); - if (v == NULL) - c->c_errors++; - else if (*pkeywords == NULL) { - c->c_errors++; - Py_DECREF(v); - } else { - if (PyDict_GetItem(*pkeywords, v) != NULL) - com_error(c, PyExc_SyntaxError, - "duplicate keyword argument"); - else - if (PyDict_SetItem(*pkeywords, v, v) != 0) - c->c_errors++; - com_addoparg(c, LOAD_CONST, com_addconst(c, v)); - com_push(c, 1); - Py_DECREF(v); - } - } - com_node(c, CHILD(n, 2)); -} - -static void -com_call_function(struct compiling *c, node *n) -{ - if (TYPE(n) == RPAR) { - com_addoparg(c, CALL_FUNCTION, 0); - } - else { - PyObject *keywords = NULL; - int i, na, nk; - int lineno = n->n_lineno; - int star_flag = 0; - int starstar_flag = 0; - int opcode; - REQ(n, arglist); - na = 0; - nk = 0; - for (i = 0; i < NCH(n); i += 2) { - node *ch = CHILD(n, i); - if (TYPE(ch) == STAR || - TYPE(ch) == DOUBLESTAR) - break; - if (ch->n_lineno != lineno) { - lineno = ch->n_lineno; - com_set_lineno(c, lineno); - } - com_argument(c, ch, &keywords); - if (keywords == NULL) - na++; - else - nk++; - } - Py_XDECREF(keywords); - while (i < NCH(n)) { - node *tok = CHILD(n, i); - node *ch = CHILD(n, i+1); - i += 3; - switch (TYPE(tok)) { - case STAR: star_flag = 1; break; - case DOUBLESTAR: starstar_flag = 1; break; - } - com_node(c, ch); - } - if (na > 255 || nk > 255) { - com_error(c, PyExc_SyntaxError, - "more than 255 arguments"); - } - if (star_flag || starstar_flag) - opcode = CALL_FUNCTION_VAR - 1 + - star_flag + (starstar_flag << 1); - else - opcode = CALL_FUNCTION; - com_addoparg(c, opcode, na | (nk << 8)); - com_pop(c, na + 2*nk + star_flag + starstar_flag); - } -} - -static void -com_select_member(struct compiling *c, node *n) -{ - com_addopname(c, LOAD_ATTR, n); -} - -static void -com_sliceobj(struct compiling *c, node *n) -{ - int i=0; - int ns=2; /* number of slice arguments */ - node *ch; - - /* first argument */ - if (TYPE(CHILD(n,i)) == COLON) { - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - i++; - } - else { - com_node(c, CHILD(n,i)); - i++; - REQ(CHILD(n,i),COLON); - i++; - } - /* second argument */ - if (i < NCH(n) && TYPE(CHILD(n,i)) == test) { - com_node(c, CHILD(n,i)); - i++; - } - else { - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - } - /* remaining arguments */ - for (; i < NCH(n); i++) { - ns++; - ch=CHILD(n,i); - REQ(ch, sliceop); - if (NCH(ch) == 1) { - /* right argument of ':' missing */ - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - } - else - com_node(c, CHILD(ch,1)); - } - com_addoparg(c, BUILD_SLICE, ns); - com_pop(c, 1 + (ns == 3)); -} - -static void -com_subscript(struct compiling *c, node *n) -{ - node *ch; - REQ(n, subscript); - ch = CHILD(n,0); - /* check for rubber index */ - if (TYPE(ch) == DOT && TYPE(CHILD(n,1)) == DOT) { - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_Ellipsis)); - com_push(c, 1); - } - else { - /* check for slice */ - if ((TYPE(ch) == COLON || NCH(n) > 1)) - com_sliceobj(c, n); - else { - REQ(ch, test); - com_node(c, ch); - } - } -} - -static void -com_subscriptlist(struct compiling *c, node *n, int assigning, node *augn) -{ - int i, op; - REQ(n, subscriptlist); - /* Check to make backward compatible slice behavior for '[i:j]' */ - if (NCH(n) == 1) { - node *sub = CHILD(n, 0); /* subscript */ - /* 'Basic' slice, should have exactly one colon. */ - if ((TYPE(CHILD(sub, 0)) == COLON - || (NCH(sub) > 1 && TYPE(CHILD(sub, 1)) == COLON)) - && (TYPE(CHILD(sub,NCH(sub)-1)) != sliceop)) - { - switch (assigning) { - case OP_DELETE: - op = DELETE_SLICE; - break; - case OP_ASSIGN: - op = STORE_SLICE; - break; - case OP_APPLY: - op = SLICE; - break; - default: - com_augassign_slice(c, sub, assigning, augn); - return; - } - com_slice(c, sub, op); - if (op == STORE_SLICE) - com_pop(c, 2); - else if (op == DELETE_SLICE) - com_pop(c, 1); - return; - } - } - /* Else normal subscriptlist. Compile each subscript. */ - for (i = 0; i < NCH(n); i += 2) - com_subscript(c, CHILD(n, i)); - /* Put multiple subscripts into a tuple */ - if (NCH(n) > 1) { - i = (NCH(n)+1) / 2; - com_addoparg(c, BUILD_TUPLE, i); - com_pop(c, i-1); - } - switch (assigning) { - case OP_DELETE: - op = DELETE_SUBSCR; - i = 2; - break; - default: - case OP_ASSIGN: - op = STORE_SUBSCR; - i = 3; - break; - case OP_APPLY: - op = BINARY_SUBSCR; - i = 1; - break; - } - if (assigning > OP_APPLY) { - com_addoparg(c, DUP_TOPX, 2); - com_push(c, 2); - com_addbyte(c, BINARY_SUBSCR); - com_pop(c, 1); - com_node(c, augn); - com_addbyte(c, assigning); - com_pop(c, 1); - com_addbyte(c, ROT_THREE); - } - com_addbyte(c, op); - com_pop(c, i); -} - -static void -com_apply_trailer(struct compiling *c, node *n) -{ - REQ(n, trailer); - switch (TYPE(CHILD(n, 0))) { - case LPAR: - com_call_function(c, CHILD(n, 1)); - break; - case DOT: - com_select_member(c, CHILD(n, 1)); - break; - case LSQB: - com_subscriptlist(c, CHILD(n, 1), OP_APPLY, NULL); - break; - default: - com_error(c, PyExc_SystemError, - "com_apply_trailer: unknown trailer type"); - } -} - -static void -com_power(struct compiling *c, node *n) -{ - int i; - REQ(n, power); - com_atom(c, CHILD(n, 0)); - for (i = 1; i < NCH(n); i++) { - if (TYPE(CHILD(n, i)) == DOUBLESTAR) { - com_factor(c, CHILD(n, i+1)); - com_addbyte(c, BINARY_POWER); - com_pop(c, 1); - break; - } - else - com_apply_trailer(c, CHILD(n, i)); - } -} - -static void -com_invert_constant(struct compiling *c, node *n) -{ - /* Compute the inverse of int and longs and use them directly, - but be prepared to generate code for all other - possibilities (invalid numbers, floats, complex). - */ - PyObject *num, *inv = NULL; - int i; - - REQ(n, NUMBER); - num = parsenumber(c, STR(n)); - if (num == NULL) - i = 255; - else { - inv = PyNumber_Invert(num); - if (inv == NULL) { - PyErr_Clear(); - i = com_addconst(c, num); - } else { - i = com_addconst(c, inv); - Py_DECREF(inv); - } - Py_DECREF(num); - } - com_addoparg(c, LOAD_CONST, i); - com_push(c, 1); - if (num != NULL && inv == NULL) - com_addbyte(c, UNARY_INVERT); -} - -static int -is_float_zero(const char *p) -{ - int found_radix_point = 0; - int ch; - while ((ch = Py_CHARMASK(*p++)) != '\0') { - switch (ch) { - case '0': - /* no reason to believe it's not 0 -- continue */ - break; - - case 'e': case 'E': case 'j': case 'J': - /* If this was a hex constant, we already would have - returned 0 due to the 'x' or 'X', so 'e' or 'E' - must be an exponent marker, and we haven't yet - seen a non-zero digit, and it doesn't matter what - the exponent is then. For 'j' or 'J' similarly, - except that this is an imaginary 0 then. */ - return 1; - - case '.': - found_radix_point = 1; - break; - - default: - return 0; - } - } - return found_radix_point; -} - -static void -com_factor(struct compiling *c, node *n) -{ - int childtype = TYPE(CHILD(n, 0)); - node *pfactor, *ppower, *patom, *pnum; - REQ(n, factor); - /* If the unary +, -, or ~ operator is applied to a constant, - don't generate a UNARY_xxx opcode. Just store the - approriate value as a constant. If the value is negative, - extend the string containing the constant and insert a - negative in the 0th position -- unless we're doing unary minus - of a floating zero! In that case the sign is significant, but - the const dict can't distinguish +0.0 from -0.0. - */ - if ((childtype == PLUS || childtype == MINUS || childtype == TILDE) - && 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 - && !(childtype == MINUS && - (STR(pnum)[0] == '0' || is_float_zero(STR(pnum))))) { - if (childtype == TILDE) { - com_invert_constant(c, pnum); - return; - } - if (childtype == MINUS) { - char *s = PyObject_MALLOC(strlen(STR(pnum)) + 2); - if (s == NULL) { - com_error(c, PyExc_MemoryError, ""); - com_addbyte(c, 255); - return; - } - s[0] = '-'; - strcpy(s + 1, STR(pnum)); - PyObject_FREE(STR(pnum)); - STR(pnum) = s; - } - com_atom(c, patom); - } - else if (childtype == PLUS) { - com_factor(c, CHILD(n, 1)); - com_addbyte(c, UNARY_POSITIVE); - } - else if (childtype == MINUS) { - com_factor(c, CHILD(n, 1)); - com_addbyte(c, UNARY_NEGATIVE); - } - else if (childtype == TILDE) { - com_factor(c, CHILD(n, 1)); - com_addbyte(c, UNARY_INVERT); - } - else { - com_power(c, CHILD(n, 0)); - } -} - -static void -com_term(struct compiling *c, node *n) -{ - int i; - int op; - REQ(n, term); - com_factor(c, CHILD(n, 0)); - for (i = 2; i < NCH(n); i += 2) { - com_factor(c, CHILD(n, i)); - switch (TYPE(CHILD(n, i-1))) { - case STAR: - op = BINARY_MULTIPLY; - break; - case SLASH: - if (c->c_flags & CO_FUTURE_DIVISION) - op = BINARY_TRUE_DIVIDE; - else - op = BINARY_DIVIDE; - break; - case PERCENT: - op = BINARY_MODULO; - break; - case DOUBLESLASH: - op = BINARY_FLOOR_DIVIDE; - break; - default: - com_error(c, PyExc_SystemError, - "com_term: operator not *, /, // or %"); - op = 255; - } - com_addbyte(c, op); - com_pop(c, 1); - } -} - -static void -com_arith_expr(struct compiling *c, node *n) -{ - int i; - int op; - REQ(n, arith_expr); - com_term(c, CHILD(n, 0)); - for (i = 2; i < NCH(n); i += 2) { - com_term(c, CHILD(n, i)); - switch (TYPE(CHILD(n, i-1))) { - case PLUS: - op = BINARY_ADD; - break; - case MINUS: - op = BINARY_SUBTRACT; - break; - default: - com_error(c, PyExc_SystemError, - "com_arith_expr: operator not + or -"); - op = 255; - } - com_addbyte(c, op); - com_pop(c, 1); - } -} - -static void -com_shift_expr(struct compiling *c, node *n) -{ - int i; - int op; - REQ(n, shift_expr); - com_arith_expr(c, CHILD(n, 0)); - for (i = 2; i < NCH(n); i += 2) { - com_arith_expr(c, CHILD(n, i)); - switch (TYPE(CHILD(n, i-1))) { - case LEFTSHIFT: - op = BINARY_LSHIFT; - break; - case RIGHTSHIFT: - op = BINARY_RSHIFT; - break; - default: - com_error(c, PyExc_SystemError, - "com_shift_expr: operator not << or >>"); - op = 255; - } - com_addbyte(c, op); - com_pop(c, 1); - } -} - -static void -com_and_expr(struct compiling *c, node *n) -{ - int i; - int op; - REQ(n, and_expr); - com_shift_expr(c, CHILD(n, 0)); - for (i = 2; i < NCH(n); i += 2) { - com_shift_expr(c, CHILD(n, i)); - if (TYPE(CHILD(n, i-1)) == AMPER) { - op = BINARY_AND; - } - else { - com_error(c, PyExc_SystemError, - "com_and_expr: operator not &"); - op = 255; - } - com_addbyte(c, op); - com_pop(c, 1); - } -} - -static void -com_xor_expr(struct compiling *c, node *n) -{ - int i; - int op; - REQ(n, xor_expr); - com_and_expr(c, CHILD(n, 0)); - for (i = 2; i < NCH(n); i += 2) { - com_and_expr(c, CHILD(n, i)); - if (TYPE(CHILD(n, i-1)) == CIRCUMFLEX) { - op = BINARY_XOR; - } - else { - com_error(c, PyExc_SystemError, - "com_xor_expr: operator not ^"); - op = 255; - } - com_addbyte(c, op); - com_pop(c, 1); - } -} - -static void -com_expr(struct compiling *c, node *n) -{ - int i; - int op; - REQ(n, expr); - com_xor_expr(c, CHILD(n, 0)); - for (i = 2; i < NCH(n); i += 2) { - com_xor_expr(c, CHILD(n, i)); - if (TYPE(CHILD(n, i-1)) == VBAR) { - op = BINARY_OR; - } - else { - com_error(c, PyExc_SystemError, - "com_expr: expr operator not |"); - op = 255; - } - com_addbyte(c, op); - com_pop(c, 1); - } -} - -static enum cmp_op -cmp_type(node *n) -{ - REQ(n, comp_op); - /* comp_op: '<' | '>' | '>=' | '<=' | '<>' | '!=' | '==' - | 'in' | 'not' 'in' | 'is' | 'is' not' */ - if (NCH(n) == 1) { - n = CHILD(n, 0); - switch (TYPE(n)) { - case LESS: return PyCmp_LT; - case GREATER: return PyCmp_GT; - case EQEQUAL: return PyCmp_EQ; - case LESSEQUAL: return PyCmp_LE; - case GREATEREQUAL: return PyCmp_GE; - case NOTEQUAL: return PyCmp_NE; /* <> or != */ - case NAME: if (strcmp(STR(n), "in") == 0) return PyCmp_IN; - if (strcmp(STR(n), "is") == 0) return PyCmp_IS; - } - } - else if (NCH(n) == 2) { - switch (TYPE(CHILD(n, 0))) { - case NAME: if (strcmp(STR(CHILD(n, 1)), "in") == 0) - return PyCmp_NOT_IN; - if (strcmp(STR(CHILD(n, 0)), "is") == 0) - return PyCmp_IS_NOT; - } - } - return PyCmp_BAD; -} - -static void -com_comparison(struct compiling *c, node *n) -{ - int i; - enum cmp_op op; - int anchor; - REQ(n, comparison); /* comparison: expr (comp_op expr)* */ - com_expr(c, CHILD(n, 0)); - if (NCH(n) == 1) - return; - - /**************************************************************** - The following code is generated for all but the last - comparison in a chain: - - label: on stack: opcode: jump to: - - a - a, b DUP_TOP - a, b, b ROT_THREE - b, a, b COMPARE_OP - b, 0-or-1 JUMP_IF_FALSE L1 - b, 1 POP_TOP - b - - We are now ready to repeat this sequence for the next - comparison in the chain. - - For the last we generate: - - b - b, c COMPARE_OP - 0-or-1 - - If there were any jumps to L1 (i.e., there was more than one - comparison), we generate: - - 0-or-1 JUMP_FORWARD L2 - L1: b, 0 ROT_TWO - 0, b POP_TOP - 0 - L2: 0-or-1 - ****************************************************************/ - - anchor = 0; - - for (i = 2; i < NCH(n); i += 2) { - com_expr(c, CHILD(n, i)); - if (i+2 < NCH(n)) { - com_addbyte(c, DUP_TOP); - com_push(c, 1); - com_addbyte(c, ROT_THREE); - } - op = cmp_type(CHILD(n, i-1)); - if (op == PyCmp_BAD) { - com_error(c, PyExc_SystemError, - "com_comparison: unknown comparison op"); - } - com_addoparg(c, COMPARE_OP, op); - com_pop(c, 1); - if (i+2 < NCH(n)) { - com_addfwref(c, JUMP_IF_FALSE, &anchor); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - } - } - - if (anchor) { - int anchor2 = 0; - com_addfwref(c, JUMP_FORWARD, &anchor2); - com_backpatch(c, anchor); - com_addbyte(c, ROT_TWO); - com_addbyte(c, POP_TOP); - com_backpatch(c, anchor2); - } -} - -static void -com_not_test(struct compiling *c, node *n) -{ - REQ(n, not_test); /* 'not' not_test | comparison */ - if (NCH(n) == 1) { - com_comparison(c, CHILD(n, 0)); - } - else { - com_not_test(c, CHILD(n, 1)); - com_addbyte(c, UNARY_NOT); - } -} - -static void -com_and_test(struct compiling *c, node *n) -{ - int i; - int anchor; - REQ(n, and_test); /* not_test ('and' not_test)* */ - anchor = 0; - i = 0; - for (;;) { - com_not_test(c, CHILD(n, i)); - if ((i += 2) >= NCH(n)) - break; - com_addfwref(c, JUMP_IF_FALSE, &anchor); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - } - if (anchor) - com_backpatch(c, anchor); -} - -static int -com_make_closure(struct compiling *c, PyCodeObject *co) -{ - int i, free = PyCode_GetNumFree(co); - if (free == 0) - return 0; - for (i = 0; i < free; ++i) { - /* Bypass com_addop_varname because it will generate - LOAD_DEREF but LOAD_CLOSURE is needed. - */ - PyObject *name = PyTuple_GET_ITEM(co->co_freevars, i); - int arg, reftype; - - /* Special case: If a class contains a method with a - free variable that has the same name as a method, - the name will be considered free *and* local in the - class. It should be handled by the closure, as - well as by the normal name loookup logic. - */ - reftype = get_ref_type(c, PyString_AS_STRING(name)); - if (reftype == CELL) - arg = com_lookup_arg(c->c_cellvars, name); - else /* (reftype == FREE) */ - arg = com_lookup_arg(c->c_freevars, name); - if (arg == -1) { - fprintf(stderr, "lookup %s in %s %d %d\n" - "freevars of %s: %s\n", - PyObject_REPR(name), - c->c_name, - reftype, arg, - PyString_AS_STRING(co->co_name), - PyObject_REPR(co->co_freevars)); - Py_FatalError("com_make_closure()"); - } - com_addoparg(c, LOAD_CLOSURE, arg); - - } - com_push(c, free); - return 1; -} - -static void -com_test(struct compiling *c, node *n) -{ - REQ(n, test); /* and_test ('or' and_test)* | lambdef */ - if (NCH(n) == 1 && TYPE(CHILD(n, 0)) == lambdef) { - PyCodeObject *co; - int i, closure; - int ndefs = com_argdefs(c, CHILD(n, 0)); - symtable_enter_scope(c->c_symtable, "lambda", lambdef, - n->n_lineno); - co = icompile(CHILD(n, 0), c); - if (co == NULL) { - c->c_errors++; - return; - } - symtable_exit_scope(c->c_symtable); - i = com_addconst(c, (PyObject *)co); - closure = com_make_closure(c, co); - com_addoparg(c, LOAD_CONST, i); - com_push(c, 1); - if (closure) { - com_addoparg(c, MAKE_CLOSURE, ndefs); - com_pop(c, PyCode_GetNumFree(co)); - } else - com_addoparg(c, MAKE_FUNCTION, ndefs); - Py_DECREF(co); - com_pop(c, ndefs); - } - else { - int anchor = 0; - int i = 0; - for (;;) { - com_and_test(c, CHILD(n, i)); - if ((i += 2) >= NCH(n)) - break; - com_addfwref(c, JUMP_IF_TRUE, &anchor); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - } - if (anchor) - com_backpatch(c, anchor); - } -} - -static void -com_list(struct compiling *c, node *n, int toplevel) -{ - /* exprlist: expr (',' expr)* [',']; likewise for testlist */ - if (NCH(n) == 1 && !toplevel) { - com_node(c, CHILD(n, 0)); - } - else { - int i; - int len; - len = (NCH(n) + 1) / 2; - for (i = 0; i < NCH(n); i += 2) - com_node(c, CHILD(n, i)); - com_addoparg(c, BUILD_TUPLE, len); - com_pop(c, len-1); - } -} - - -/* Begin of assignment compilation */ - - -static void -com_augassign_attr(struct compiling *c, node *n, int opcode, node *augn) -{ - com_addbyte(c, DUP_TOP); - com_push(c, 1); - com_addopname(c, LOAD_ATTR, n); - com_node(c, augn); - com_addbyte(c, opcode); - com_pop(c, 1); - com_addbyte(c, ROT_TWO); - com_addopname(c, STORE_ATTR, n); - com_pop(c, 2); -} - -static void -com_assign_attr(struct compiling *c, node *n, int assigning) -{ - if (none_assignment_check(c, STR(n), assigning)) - return; - com_addopname(c, assigning ? STORE_ATTR : DELETE_ATTR, n); - com_pop(c, assigning ? 2 : 1); -} - -static void -com_assign_trailer(struct compiling *c, node *n, int assigning, node *augn) -{ - REQ(n, trailer); - switch (TYPE(CHILD(n, 0))) { - case LPAR: /* '(' [exprlist] ')' */ - if (assigning == OP_DELETE) - com_error(c, PyExc_SyntaxError, - "can't delete function call"); - else - com_error(c, PyExc_SyntaxError, - "can't assign to function call"); - break; - case DOT: /* '.' NAME */ - if (assigning > OP_APPLY) - com_augassign_attr(c, CHILD(n, 1), assigning, augn); - else - com_assign_attr(c, CHILD(n, 1), assigning); - break; - case LSQB: /* '[' subscriptlist ']' */ - com_subscriptlist(c, CHILD(n, 1), assigning, augn); - break; - default: - com_error(c, PyExc_SystemError, "unknown trailer type"); - } -} - -static void -com_assign_sequence(struct compiling *c, node *n, int assigning) -{ - int i; - if (TYPE(n) != testlist && TYPE(n) != testlist_gexp && - TYPE(n) != listmaker) - REQ(n, exprlist); - if (assigning) { - i = (NCH(n)+1)/2; - com_addoparg(c, UNPACK_SEQUENCE, i); - com_push(c, i-1); - } - for (i = 0; i < NCH(n); i += 2) - com_assign(c, CHILD(n, i), assigning, NULL); -} - -static void -com_augassign_name(struct compiling *c, node *n, int opcode, node *augn) -{ - REQ(n, NAME); - com_addop_varname(c, VAR_LOAD, STR(n)); - com_push(c, 1); - com_node(c, augn); - com_addbyte(c, opcode); - com_pop(c, 1); - com_assign_name(c, n, OP_ASSIGN); -} - -static void -com_assign_name(struct compiling *c, node *n, int assigning) -{ - REQ(n, NAME); - com_addop_varname(c, assigning ? VAR_STORE : VAR_DELETE, STR(n)); - if (assigning) - com_pop(c, 1); -} - -static void -com_assign(struct compiling *c, node *n, int assigning, node *augn) -{ - /* Loop to avoid trivial recursion */ - for (;;) { - switch (TYPE(n)) { - - case exprlist: - case testlist: - case testlist1: - case testlist_gexp: - if (NCH(n) > 1) { - if (TYPE(CHILD(n, 1)) == gen_for) { - com_error(c, PyExc_SyntaxError, - "assign to generator expression not possible"); - return; - } - if (assigning > OP_APPLY) { - com_error(c, PyExc_SyntaxError, - "augmented assign to generator expression not possible"); - return; - } - com_assign_sequence(c, n, assigning); - return; - } - n = CHILD(n, 0); - break; - case yield_expr: - com_error(c, PyExc_SyntaxError, - "assignment to yield expression not possible"); - return; - - case test: - case and_test: - case not_test: - case comparison: - case expr: - case xor_expr: - case and_expr: - case shift_expr: - case arith_expr: - case term: - case factor: - if (NCH(n) > 1) { - com_error(c, PyExc_SyntaxError, - "can't assign to operator"); - return; - } - n = CHILD(n, 0); - break; - - case power: /* atom trailer* ('**' power)* - ('+'|'-'|'~') factor | atom trailer* */ - if (TYPE(CHILD(n, 0)) != atom) { - com_error(c, PyExc_SyntaxError, - "can't assign to operator"); - return; - } - if (NCH(n) > 1) { /* trailer or exponent present */ - int i; - com_node(c, CHILD(n, 0)); - for (i = 1; i+1 < NCH(n); i++) { - if (TYPE(CHILD(n, i)) == DOUBLESTAR) { - com_error(c, PyExc_SyntaxError, - "can't assign to operator"); - return; - } - com_apply_trailer(c, CHILD(n, i)); - } /* NB i is still alive */ - com_assign_trailer(c, - CHILD(n, i), assigning, augn); - return; - } - n = CHILD(n, 0); - break; - - case atom: - switch (TYPE(CHILD(n, 0))) { - case LPAR: - n = CHILD(n, 1); - if (TYPE(n) == RPAR) { - /* XXX Should allow () = () ??? */ - com_error(c, PyExc_SyntaxError, - "can't assign to ()"); - return; - } - if (assigning > OP_APPLY) { - com_error(c, PyExc_SyntaxError, - "augmented assign to tuple literal, yield, or generator expression not possible"); - return; - } - break; - case LSQB: - n = CHILD(n, 1); - if (TYPE(n) == RSQB) { - com_error(c, PyExc_SyntaxError, - "can't assign to []"); - return; - } - if (assigning > OP_APPLY) { - com_error(c, PyExc_SyntaxError, - "augmented assign to list literal or comprehension not possible"); - return; - } - if (NCH(n) > 1 - && TYPE(CHILD(n, 1)) == list_for) { - com_error(c, PyExc_SyntaxError, - "can't assign to list comprehension"); - return; - } - com_assign_sequence(c, n, assigning); - return; - case NAME: - if (assigning > OP_APPLY) - com_augassign_name(c, CHILD(n, 0), - assigning, augn); - else - com_assign_name(c, CHILD(n, 0), - assigning); - return; - default: - com_error(c, PyExc_SyntaxError, - "can't assign to literal"); - return; - } - break; - - case lambdef: - com_error(c, PyExc_SyntaxError, - "can't assign to lambda"); - return; - - default: - com_error(c, PyExc_SystemError, - "com_assign: bad node"); - return; - - } - } -} - -static void -com_augassign(struct compiling *c, node *n) -{ - int opcode; - - switch (STR(CHILD(CHILD(n, 1), 0))[0]) { - case '+': opcode = INPLACE_ADD; break; - case '-': opcode = INPLACE_SUBTRACT; break; - case '/': - if (STR(CHILD(CHILD(n, 1), 0))[1] == '/') - opcode = INPLACE_FLOOR_DIVIDE; - else if (c->c_flags & CO_FUTURE_DIVISION) - opcode = INPLACE_TRUE_DIVIDE; - else - opcode = INPLACE_DIVIDE; - break; - case '%': opcode = INPLACE_MODULO; break; - case '<': opcode = INPLACE_LSHIFT; break; - case '>': opcode = INPLACE_RSHIFT; break; - case '&': opcode = INPLACE_AND; break; - case '^': opcode = INPLACE_XOR; break; - case '|': opcode = INPLACE_OR; break; - case '*': - if (STR(CHILD(CHILD(n, 1), 0))[1] == '*') - opcode = INPLACE_POWER; - else - opcode = INPLACE_MULTIPLY; - break; - default: - com_error(c, PyExc_SystemError, "com_augassign: bad operator"); - return; - } - com_assign(c, CHILD(n, 0), opcode, CHILD(n, 2)); -} - -static void -com_expr_stmt(struct compiling *c, node *n) -{ - REQ(n, expr_stmt); - /* testlist (('=' testlist)* | augassign testlist) */ - /* Forget it if we have just a doc string here */ - if (!c->c_interactive && NCH(n) == 1 && get_rawdocstring(n) != NULL) - return; - if (NCH(n) == 1) { - com_node(c, CHILD(n, NCH(n)-1)); - if (c->c_interactive) - com_addbyte(c, PRINT_EXPR); - else - com_addbyte(c, POP_TOP); - com_pop(c, 1); - } - else if (TYPE(CHILD(n,1)) == augassign) - com_augassign(c, n); - else { - int i; - com_node(c, CHILD(n, NCH(n)-1)); - for (i = 0; i < NCH(n)-2; i+=2) { - if (i+2 < NCH(n)-2) { - com_addbyte(c, DUP_TOP); - com_push(c, 1); - } - com_assign(c, CHILD(n, i), OP_ASSIGN, NULL); - } - } -} - -static void -com_assert_stmt(struct compiling *c, node *n) -{ - int a = 0; - int i; - REQ(n, assert_stmt); /* 'assert' test [',' test] */ - if (Py_OptimizeFlag) - return; - /* Generate code like - - if not : - raise AssertionError [, ] - - where is the second test, if present. - */ - com_node(c, CHILD(n, 1)); - com_addfwref(c, JUMP_IF_TRUE, &a); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - /* Raise that exception! */ - com_addop_name(c, LOAD_GLOBAL, "AssertionError"); - com_push(c, 1); - i = NCH(n)/2; /* Either 2 or 4 */ - if (i > 1) - com_node(c, CHILD(n, 3)); - com_addoparg(c, RAISE_VARARGS, i); - com_pop(c, i); - /* The interpreter does not fall through */ - /* Jump ends up here */ - com_backpatch(c, a); - com_addbyte(c, POP_TOP); -} - -static void -com_print_stmt(struct compiling *c, node *n) -{ - int i = 1; - node* stream = NULL; - - REQ(n, print_stmt); /* 'print' (test ',')* [test] */ - - /* are we using the extended print form? */ - if (NCH(n) >= 2 && TYPE(CHILD(n, 1)) == RIGHTSHIFT) { - stream = CHILD(n, 2); - com_node(c, stream); - /* stack: [...] => [... stream] */ - com_push(c, 1); - if (NCH(n) > 3 && TYPE(CHILD(n, 3)) == COMMA) - i = 4; - else - i = 3; - } - for (; i < NCH(n); i += 2) { - if (stream != NULL) { - com_addbyte(c, DUP_TOP); - /* stack: [stream] => [stream stream] */ - com_push(c, 1); - com_node(c, CHILD(n, i)); - /* stack: [stream stream] => [stream stream obj] */ - com_addbyte(c, ROT_TWO); - /* stack: [stream stream obj] => [stream obj stream] */ - com_addbyte(c, PRINT_ITEM_TO); - /* stack: [stream obj stream] => [stream] */ - com_pop(c, 2); - } - else { - com_node(c, CHILD(n, i)); - /* stack: [...] => [... obj] */ - com_addbyte(c, PRINT_ITEM); - com_pop(c, 1); - } - } - /* XXX Alternatively, LOAD_CONST '\n' and then PRINT_ITEM */ - if (TYPE(CHILD(n, NCH(n)-1)) == COMMA) { - if (stream != NULL) { - /* must pop the extra stream object off the stack */ - com_addbyte(c, POP_TOP); - /* stack: [... stream] => [...] */ - com_pop(c, 1); - } - } - else { - if (stream != NULL) { - /* this consumes the last stream object on stack */ - com_addbyte(c, PRINT_NEWLINE_TO); - /* stack: [... stream] => [...] */ - com_pop(c, 1); - } - else - com_addbyte(c, PRINT_NEWLINE); - } -} - -static void -com_return_stmt(struct compiling *c, node *n) -{ - REQ(n, return_stmt); /* 'return' [testlist] */ - if (!c->c_infunction) { - com_error(c, PyExc_SyntaxError, "'return' outside function"); - } - if (c->c_flags & CO_GENERATOR) { - if (NCH(n) > 1) { - com_error(c, PyExc_SyntaxError, - "'return' with argument inside generator"); - } - } - if (NCH(n) < 2) { - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - } - else - com_node(c, CHILD(n, 1)); - com_addbyte(c, RETURN_VALUE); - com_pop(c, 1); -} - -static void -com_yield_expr(struct compiling *c, node *n) -{ - REQ(n, yield_expr); /* 'yield' testlist */ - if (!c->c_infunction) { - com_error(c, PyExc_SyntaxError, "'yield' outside function"); - } - - /* for (i = 0; i < c->c_nblocks; ++i) { - if (c->c_block[i] == SETUP_FINALLY) { - com_error(c, PyExc_SyntaxError, - "'yield' not allowed in a 'try' block " - "with a 'finally' clause"); - return; - } - } */ - - if (NCH(n) < 2) { - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - } - else - com_node(c, CHILD(n, 1)); - com_addbyte(c, YIELD_VALUE); -} - -static void -com_yield_stmt(struct compiling *c, node *n) -{ - REQ(n, yield_stmt); /* yield_expr */ - com_node(c, CHILD(n, 0)); - com_addbyte(c, POP_TOP); - com_pop(c, 1); -} - - -static void -com_raise_stmt(struct compiling *c, node *n) -{ - int i; - REQ(n, raise_stmt); /* 'raise' [test [',' test [',' test]]] */ - if (NCH(n) > 1) { - com_node(c, CHILD(n, 1)); - if (NCH(n) > 3) { - com_node(c, CHILD(n, 3)); - if (NCH(n) > 5) - com_node(c, CHILD(n, 5)); - } - } - i = NCH(n)/2; - com_addoparg(c, RAISE_VARARGS, i); - com_pop(c, i); -} - -static void -com_from_import(struct compiling *c, node *n) -{ - com_addopname(c, IMPORT_FROM, CHILD(n, 0)); - com_push(c, 1); - if (NCH(n) > 1) { - if (strcmp(STR(CHILD(n, 1)), "as") != 0) { - com_error(c, PyExc_SyntaxError, "invalid syntax"); - return; - } - com_addop_varname(c, VAR_STORE, STR(CHILD(n, 2))); - } else - com_addop_varname(c, VAR_STORE, STR(CHILD(n, 0))); - com_pop(c, 1); -} + case UNARY_POSITIVE: + case UNARY_NEGATIVE: + case UNARY_NOT: + case UNARY_CONVERT: + case UNARY_INVERT: + return 0; -static void -com_import_stmt(struct compiling *c, node *n) -{ - node *nn; - int i; - REQ(n, import_stmt); - n = CHILD(n, 0); - /* import_stmt: import_name | import_from */ - if (TYPE(n) == import_from) { - /* 'from' dotted_name 'import' ('*' | - '(' import_as_names ')' | import_as_names) */ - PyObject *tup; - REQ(CHILD(n, 1), dotted_name); - nn = CHILD(n, 3 + (TYPE(CHILD(n, 3)) == LPAR)); - if (TYPE(nn) == STAR) - tup = Py_BuildValue("(s)", "*"); - else { - if (TYPE(CHILD(nn, NCH(nn) - 1)) == COMMA && - TYPE(CHILD(n, 3)) != LPAR) { - com_error(c, PyExc_SyntaxError, - "trailing comma not allowed " - "without surrounding parentheses"); - return; - } - REQ(nn, import_as_names); - tup = PyTuple_New((NCH(nn) + 1) / 2); - for (i = 0; i < NCH(nn); i += 2) { - PyObject *s = PyString_FromString( - STR(CHILD(CHILD(nn, i), 0))); - if (s == NULL) { - Py_CLEAR(tup); - break; - } else - PyTuple_SET_ITEM(tup, i / 2, s); - } - if (tup == NULL) { - /* Assume that failue above was MemoryError */ - com_error(c, PyExc_MemoryError, ""); - return; - } - } - com_addoparg(c, LOAD_CONST, com_addconst(c, tup)); - Py_DECREF(tup); - com_push(c, 1); - com_addopname(c, IMPORT_NAME, CHILD(n, 1)); - if (TYPE(nn) == STAR) - com_addbyte(c, IMPORT_STAR); - else { - for (i = 0; i < NCH(nn); i += 2) - com_from_import(c, CHILD(nn, i)); - com_addbyte(c, POP_TOP); - } - com_pop(c, 1); - } - else { - /* 'import' dotted_as_names */ - nn = CHILD(n, 1); - REQ(nn, dotted_as_names); - for (i = 0; i < NCH(nn); i += 2) { - node *subn = CHILD(nn, i); - REQ(subn, dotted_as_name); - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - com_addopname(c, IMPORT_NAME, CHILD(subn, 0)); - if (NCH(subn) > 1) { - int j; - if (strcmp(STR(CHILD(subn, 1)), "as") != 0) { - com_error(c, PyExc_SyntaxError, - "invalid syntax"); - return; - } - for (j=2 ; j < NCH(CHILD(subn, 0)); j += 2) - com_addopname(c, LOAD_ATTR, - CHILD(CHILD(subn, 0), - j)); - com_addop_varname(c, VAR_STORE, - STR(CHILD(subn, 2))); - } else - com_addop_varname(c, VAR_STORE, - STR(CHILD(CHILD(subn, 0), - 0))); - com_pop(c, 1); - } - } -} + case BINARY_POWER: + case BINARY_MULTIPLY: + case BINARY_DIVIDE: + case BINARY_MODULO: + case BINARY_ADD: + case BINARY_SUBTRACT: + case BINARY_SUBSCR: + case BINARY_FLOOR_DIVIDE: + case BINARY_TRUE_DIVIDE: + return -1; + case INPLACE_FLOOR_DIVIDE: + case INPLACE_TRUE_DIVIDE: + return -1; -static void -com_exec_stmt(struct compiling *c, node *n) -{ - REQ(n, exec_stmt); - /* exec_stmt: 'exec' expr ['in' expr [',' expr]] */ - com_node(c, CHILD(n, 1)); - if (NCH(n) >= 4) - com_node(c, CHILD(n, 3)); - else { - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - } - if (NCH(n) >= 6) - com_node(c, CHILD(n, 5)); - else { - com_addbyte(c, DUP_TOP); - com_push(c, 1); - } - com_addbyte(c, EXEC_STMT); - com_pop(c, 3); -} + case SLICE+0: + return 1; + case SLICE+1: + return 0; + case SLICE+2: + return 0; + case SLICE+3: + return -1; -static int -is_constant_false(struct compiling *c, node *n) -{ - PyObject *v; - int i; - /* argument c will be NULL when called from symtable_node() */ + case STORE_SLICE+0: + return -2; + case STORE_SLICE+1: + return -3; + case STORE_SLICE+2: + return -3; + case STORE_SLICE+3: + return -4; - /* Label to avoid tail recursion */ - next: - switch (TYPE(n)) { + case DELETE_SLICE+0: + return -1; + case DELETE_SLICE+1: + return -2; + case DELETE_SLICE+2: + return -2; + case DELETE_SLICE+3: + return -3; + + case INPLACE_ADD: + case INPLACE_SUBTRACT: + case INPLACE_MULTIPLY: + case INPLACE_DIVIDE: + case INPLACE_MODULO: + return -1; + case STORE_SUBSCR: + return -3; + case DELETE_SUBSCR: + return -2; - case suite: - if (NCH(n) == 1) { - n = CHILD(n, 0); - goto next; - } - /* Fall through */ - case file_input: - for (i = 0; i < NCH(n); i++) { - node *ch = CHILD(n, i); - if (TYPE(ch) == stmt) { - n = ch; - goto next; - } - } - break; + case BINARY_LSHIFT: + case BINARY_RSHIFT: + case BINARY_AND: + case BINARY_XOR: + case BINARY_OR: + return -1; + case INPLACE_POWER: + return -1; + case GET_ITER: + return 0; - case stmt: - case simple_stmt: - case small_stmt: - n = CHILD(n, 0); - goto next; - - case expr_stmt: - case testlist: - case testlist1: - case test: - case and_test: - case not_test: - case comparison: - case expr: - case xor_expr: - case and_expr: - case shift_expr: - case arith_expr: - case term: - case factor: - case power: - case atom: - if (NCH(n) == 1) { - n = CHILD(n, 0); - goto next; - } - break; + case PRINT_EXPR: + return -1; + case PRINT_ITEM: + return -1; + case PRINT_NEWLINE: + return 0; + case PRINT_ITEM_TO: + return -2; + case PRINT_NEWLINE_TO: + return -1; + case INPLACE_LSHIFT: + case INPLACE_RSHIFT: + case INPLACE_AND: + case INPLACE_XOR: + case INPLACE_OR: + return -1; + case BREAK_LOOP: + return 0; - case NAME: - if (Py_OptimizeFlag && strcmp(STR(n), "__debug__") == 0) + case LOAD_LOCALS: return 1; - break; - - case NUMBER: - v = parsenumber(c, STR(n)); - if (v == NULL) { - PyErr_Clear(); - break; - } - i = PyObject_IsTrue(v); - Py_DECREF(v); - return i == 0; + case RETURN_VALUE: + return -1; + case IMPORT_STAR: + return -1; + case EXEC_STMT: + return -3; + case YIELD_VALUE: + return 0; - case STRING: - v = parsestr(c, STR(n)); - if (v == NULL) { - PyErr_Clear(); - break; - } - i = PyObject_IsTrue(v); - Py_DECREF(v); - return i == 0; + case POP_BLOCK: + return 0; + case END_FINALLY: + return -1; /* or -2 or -3 if exception occurred */ + case BUILD_CLASS: + return -2; - } - return 0; -} + case STORE_NAME: + return -1; + case DELETE_NAME: + return 0; + case UNPACK_SEQUENCE: + return oparg-1; + case FOR_ITER: + return 1; + case STORE_ATTR: + return -2; + case DELETE_ATTR: + return -1; + case STORE_GLOBAL: + return -1; + case DELETE_GLOBAL: + return 0; + case DUP_TOPX: + return oparg; + case LOAD_CONST: + return 1; + case LOAD_NAME: + return 1; + case BUILD_TUPLE: + case BUILD_LIST: + return 1-oparg; + case BUILD_MAP: + return 1; + case LOAD_ATTR: + return 0; + case COMPARE_OP: + return -1; + case IMPORT_NAME: + return 0; + case IMPORT_FROM: + return 1; -/* Look under n for a return stmt with an expression. - * This hack is used to find illegal returns under "if 0:" blocks in - * functions already known to be generators (as determined by the symtable - * pass). - * Return the offending return node if found, else NULL. - */ -static node * -look_for_offending_return(node *n) -{ - int i; + case JUMP_FORWARD: + case JUMP_IF_FALSE: + case JUMP_IF_TRUE: + case JUMP_ABSOLUTE: + return 0; - for (i = 0; i < NCH(n); ++i) { - node *kid = CHILD(n, i); + case LOAD_GLOBAL: + return 1; - switch (TYPE(kid)) { - case classdef: - case funcdef: - case lambdef: - /* Stuff in nested functions & classes doesn't - affect the code block we started in. */ - return NULL; + case CONTINUE_LOOP: + return 0; + case SETUP_LOOP: + return 0; + case SETUP_EXCEPT: + case SETUP_FINALLY: + return 3; /* actually pushed by an exception */ - case return_stmt: - if (NCH(kid) > 1) - return kid; - break; + case LOAD_FAST: + return 1; + case STORE_FAST: + return -1; + case DELETE_FAST: + return 0; - default: { - node *bad = look_for_offending_return(kid); - if (bad != NULL) - return bad; - } - } - } + case RAISE_VARARGS: + return -oparg; +#define NARGS(o) (((o) % 256) + 2*((o) / 256)) + case CALL_FUNCTION: + return -NARGS(oparg); + case CALL_FUNCTION_VAR: + case CALL_FUNCTION_KW: + return -NARGS(oparg)-1; + case CALL_FUNCTION_VAR_KW: + return -NARGS(oparg)-2; +#undef NARGS + case MAKE_FUNCTION: + return -oparg; + case BUILD_SLICE: + if (oparg == 3) + return -2; + else + return -1; - return NULL; -} + case MAKE_CLOSURE: + return -oparg; + case LOAD_CLOSURE: + return 1; + case LOAD_DEREF: + return 1; + case STORE_DEREF: + return -1; + default: + fprintf(stderr, "opcode = %d\n", opcode); + Py_FatalError("opcode_stack_effect()"); -static void -com_if_stmt(struct compiling *c, node *n) -{ - int i; - int anchor = 0; - REQ(n, if_stmt); - /*'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] */ - for (i = 0; i+3 < NCH(n); i+=4) { - int a = 0; - node *ch = CHILD(n, i+1); - if (is_constant_false(c, ch)) { - /* We're going to skip this block. However, if this - is a generator, we have to check the dead code - anyway to make sure there aren't any return stmts - with expressions, in the same scope. */ - if (c->c_flags & CO_GENERATOR) { - node *p = look_for_offending_return(n); - if (p != NULL) { - int savelineno = c->c_lineno; - c->c_lineno = p->n_lineno; - com_error(c, PyExc_SyntaxError, - "'return' with argument " - "inside generator"); - c->c_lineno = savelineno; - } - } - continue; - } - if (i > 0) - com_set_lineno(c, ch->n_lineno); - com_node(c, ch); - com_addfwref(c, JUMP_IF_FALSE, &a); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - com_node(c, CHILD(n, i+3)); - com_addfwref(c, JUMP_FORWARD, &anchor); - com_backpatch(c, a); - /* We jump here with an extra entry which we now pop */ - com_addbyte(c, POP_TOP); } - if (i+2 < NCH(n)) - com_node(c, CHILD(n, i+2)); - if (anchor) - com_backpatch(c, anchor); -} - -static void -com_while_stmt(struct compiling *c, node *n) -{ - int break_anchor = 0; - int anchor = 0; - int save_begin = c->c_begin; - REQ(n, while_stmt); /* 'while' test ':' suite ['else' ':' suite] */ - com_addfwref(c, SETUP_LOOP, &break_anchor); - block_push(c, SETUP_LOOP); - c->c_begin = c->c_nexti; - com_set_lineno(c, n->n_lineno); - com_node(c, CHILD(n, 1)); - com_addfwref(c, JUMP_IF_FALSE, &anchor); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - c->c_loops++; - com_node(c, CHILD(n, 3)); - c->c_loops--; - com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); - c->c_begin = save_begin; - com_backpatch(c, anchor); - /* We jump here with one entry more on the stack */ - com_addbyte(c, POP_TOP); - com_addbyte(c, POP_BLOCK); - block_pop(c, SETUP_LOOP); - if (NCH(n) > 4) - com_node(c, CHILD(n, 6)); - com_backpatch(c, break_anchor); -} - -static void -com_for_stmt(struct compiling *c, node *n) -{ - int break_anchor = 0; - int anchor = 0; - int save_begin = c->c_begin; - REQ(n, for_stmt); - /* 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] */ - com_addfwref(c, SETUP_LOOP, &break_anchor); - block_push(c, SETUP_LOOP); - com_node(c, CHILD(n, 3)); - com_addbyte(c, GET_ITER); - c->c_begin = c->c_nexti; - com_set_lineno(c, c->c_last_line); - com_addfwref(c, FOR_ITER, &anchor); - com_push(c, 1); - com_assign(c, CHILD(n, 1), OP_ASSIGN, NULL); - c->c_loops++; - com_node(c, CHILD(n, 5)); - c->c_loops--; - com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); - c->c_begin = save_begin; - com_backpatch(c, anchor); - com_pop(c, 1); /* FOR_ITER has popped this */ - com_addbyte(c, POP_BLOCK); - block_pop(c, SETUP_LOOP); - if (NCH(n) > 8) - com_node(c, CHILD(n, 8)); - com_backpatch(c, break_anchor); + return 0; /* not reachable */ } -/* Code generated for "try: S finally: Sf" is as follows: - - SETUP_FINALLY L - - POP_BLOCK - LOAD_CONST - L: - END_FINALLY - - The special instructions use the block stack. Each block - stack entry contains the instruction that created it (here - SETUP_FINALLY), the level of the value stack at the time the - block stack entry was created, and a label (here L). - - SETUP_FINALLY: - Pushes the current value stack level and the label - onto the block stack. - POP_BLOCK: - Pops en entry from the block stack, and pops the value - stack until its level is the same as indicated on the - block stack. (The label is ignored.) - END_FINALLY: - Pops a variable number of entries from the *value* stack - and re-raises the exception they specify. The number of - entries popped depends on the (pseudo) exception type. - - The block stack is unwound when an exception is raised: - when a SETUP_FINALLY entry is found, the exception is pushed - onto the value stack (and the exception condition is cleared), - and the interpreter jumps to the label gotten from the block - stack. - - Code generated for "try: S except E1, V1: S1 except E2, V2: S2 ...": - (The contents of the value stack is shown in [], with the top - at the right; 'tb' is trace-back info, 'val' the exception's - associated value, and 'exc' the exception.) - - Value stack Label Instruction Argument - [] SETUP_EXCEPT L1 - [] - [] POP_BLOCK - [] JUMP_FORWARD L0 - - [tb, val, exc] L1: DUP ) - [tb, val, exc, exc] ) - [tb, val, exc, exc, E1] COMPARE_OP EXC_MATCH ) only if E1 - [tb, val, exc, 1-or-0] JUMP_IF_FALSE L2 ) - [tb, val, exc, 1] POP ) - [tb, val, exc] POP - [tb, val] (or POP if no V1) - [tb] POP - [] - JUMP_FORWARD L0 - - [tb, val, exc, 0] L2: POP - [tb, val, exc] DUP - .............................etc....................... - - [tb, val, exc, 0] Ln+1: POP - [tb, val, exc] END_FINALLY # re-raise exception - - [] L0: - - Of course, parts are not generated if Vi or Ei is not present. +/* Add an opcode with no argument. + Returns 0 on failure, 1 on success. */ -static void -com_try_except(struct compiling *c, node *n) -{ - int except_anchor = 0; - int end_anchor = 0; - int else_anchor = 0; - int i; - node *ch; - - com_addfwref(c, SETUP_EXCEPT, &except_anchor); - block_push(c, SETUP_EXCEPT); - com_node(c, CHILD(n, 2)); - com_addbyte(c, POP_BLOCK); - block_pop(c, SETUP_EXCEPT); - com_addfwref(c, JUMP_FORWARD, &else_anchor); - com_backpatch(c, except_anchor); - for (i = 3; - i < NCH(n) && TYPE(ch = CHILD(n, i)) == except_clause; - i += 3) { - /* except_clause: 'except' [expr [',' var]] */ - if (except_anchor == 0) { - com_error(c, PyExc_SyntaxError, - "default 'except:' must be last"); - break; - } - except_anchor = 0; - com_push(c, 3); /* tb, val, exc pushed by exception */ - com_set_lineno(c, ch->n_lineno); - if (NCH(ch) > 1) { - com_addbyte(c, DUP_TOP); - com_push(c, 1); - com_node(c, CHILD(ch, 1)); - com_addoparg(c, COMPARE_OP, PyCmp_EXC_MATCH); - com_pop(c, 1); - com_addfwref(c, JUMP_IF_FALSE, &except_anchor); - com_addbyte(c, POP_TOP); - com_pop(c, 1); - } - com_addbyte(c, POP_TOP); - com_pop(c, 1); - if (NCH(ch) > 3) - com_assign(c, CHILD(ch, 3), OP_ASSIGN, NULL); - else { - com_addbyte(c, POP_TOP); - com_pop(c, 1); - } - com_addbyte(c, POP_TOP); - com_pop(c, 1); - com_node(c, CHILD(n, i+2)); - com_addfwref(c, JUMP_FORWARD, &end_anchor); - if (except_anchor) { - com_backpatch(c, except_anchor); - /* We come in with [tb, val, exc, 0] on the - stack; one pop and it's the same as - expected at the start of the loop */ - com_addbyte(c, POP_TOP); - } - } - /* We actually come in here with [tb, val, exc] but the - END_FINALLY will zap those and jump around. - The c_stacklevel does not reflect them so we need not pop - anything. */ - com_addbyte(c, END_FINALLY); - com_backpatch(c, else_anchor); - if (i < NCH(n)) - com_node(c, CHILD(n, i+2)); - com_backpatch(c, end_anchor); -} - -static void -com_try_finally(struct compiling *c, node *n) -{ - int finally_anchor = 0; - node *ch; - - com_addfwref(c, SETUP_FINALLY, &finally_anchor); - block_push(c, SETUP_FINALLY); - com_node(c, CHILD(n, 2)); - com_addbyte(c, POP_BLOCK); - block_pop(c, SETUP_FINALLY); - block_push(c, END_FINALLY); - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - /* While the generated code pushes only one item, - the try-finally handling can enter here with - up to three items. OK, here are the details: - 3 for an exception, 2 for RETURN, 1 for BREAK. */ - com_push(c, 3); - com_backpatch(c, finally_anchor); - ch = CHILD(n, NCH(n)-1); - com_set_lineno(c, ch->n_lineno); - com_node(c, ch); - com_addbyte(c, END_FINALLY); - block_pop(c, END_FINALLY); - com_pop(c, 3); /* Matches the com_push above */ -} - -static void -com_try_stmt(struct compiling *c, node *n) -{ - REQ(n, try_stmt); - /* 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite] - | 'try' ':' suite 'finally' ':' suite */ - if (TYPE(CHILD(n, 3)) != except_clause) - com_try_finally(c, n); - else - com_try_except(c, n); -} - -static node * -get_rawdocstring(node *n) -{ - int i; - - /* Label to avoid tail recursion */ - next: - switch (TYPE(n)) { - - case suite: - if (NCH(n) == 1) { - n = CHILD(n, 0); - goto next; - } - /* Fall through */ - case file_input: - for (i = 0; i < NCH(n); i++) { - node *ch = CHILD(n, i); - if (TYPE(ch) == stmt) { - n = ch; - goto next; - } - } - break; - - case stmt: - case simple_stmt: - case small_stmt: - n = CHILD(n, 0); - goto next; - - case expr_stmt: - case testlist: - case testlist1: - case test: - case and_test: - case not_test: - case comparison: - case expr: - case xor_expr: - case and_expr: - case shift_expr: - case arith_expr: - case term: - case factor: - case power: - if (NCH(n) == 1) { - n = CHILD(n, 0); - goto next; - } - break; - - case atom: - if (TYPE(CHILD(n, 0)) == STRING) - return n; - break; - - } - return NULL; -} - -static PyObject * -get_docstring(struct compiling *c, node *n) -{ - /* Don't generate doc-strings if run with -OO */ - if (Py_OptimizeFlag > 1) - return NULL; - n = get_rawdocstring(n); - if (n == NULL) - return NULL; - return parsestrplus(c, n); -} - -static void -com_suite(struct compiling *c, node *n) -{ - REQ(n, suite); - /* simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT */ - if (NCH(n) == 1) { - com_node(c, CHILD(n, 0)); - } - else { - int i; - for (i = 0; i < NCH(n) && c->c_errors == 0; i++) { - node *ch = CHILD(n, i); - if (TYPE(ch) == stmt) - com_node(c, ch); - } - } +static int +compiler_addop(struct compiler *c, int opcode) +{ + basicblock *b; + struct instr *i; + int off; + off = compiler_next_instr(c, c->u->u_curblock); + if (off < 0) + return 0; + b = c->u->u_curblock; + i = &b->b_instr[off]; + i->i_opcode = opcode; + i->i_hasarg = 0; + if (opcode == RETURN_VALUE) + b->b_return = 1; + compiler_set_lineno(c, off); + return 1; } -/* ARGSUSED */ -static void -com_continue_stmt(struct compiling *c, node *n) -{ - int i = c->c_nblocks; - if (i-- > 0 && c->c_block[i] == SETUP_LOOP) { - com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); - } - else if (i <= 0) { - /* at the outer level */ - com_error(c, PyExc_SyntaxError, - "'continue' not properly in loop"); - } - else { - int j; - for (j = i-1; j >= 0; --j) { - if (c->c_block[j] == SETUP_LOOP) - break; - } - if (j >= 0) { - /* there is a loop, but something interferes */ - for (; i > j; --i) { - if (c->c_block[i] == SETUP_EXCEPT || - c->c_block[i] == SETUP_FINALLY) { - com_addoparg(c, CONTINUE_LOOP, - c->c_begin); - return; - } - if (c->c_block[i] == END_FINALLY) { - com_error(c, PyExc_SyntaxError, - "'continue' not supported inside 'finally' clause"); - return; - } - } +static int +compiler_add_o(struct compiler *c, PyObject *dict, PyObject *o) +{ + PyObject *t, *v; + int arg; + + /* necessary to make sure types aren't coerced (e.g., int and long) */ + /* XXX should use: t = PyTuple_Pack(2, o, o->ob_type); */ + t = Py_BuildValue("(OO)", o, o->ob_type); + if (t == NULL) + return -1; + + v = PyDict_GetItem(dict, t); + if (!v) { + arg = PyDict_Size(dict); + v = PyInt_FromLong(arg); + if (!v) { + Py_DECREF(t); + return -1; + } + if (PyDict_SetItem(dict, t, v) < 0) { + Py_DECREF(t); + Py_DECREF(v); + return -1; } - com_error(c, PyExc_SyntaxError, - "'continue' not properly in loop"); + Py_DECREF(v); } - /* XXX Could allow it inside a 'finally' clause - XXX if we could pop the exception still on the stack */ + else + arg = PyInt_AsLong(v); + Py_DECREF(t); + return arg; } -/* Return the number of default values in the argument list. +static int +compiler_addop_o(struct compiler *c, int opcode, PyObject *dict, + PyObject *o) +{ + int arg = compiler_add_o(c, dict, o); + if (arg < 0) + return 0; + return compiler_addop_i(c, opcode, arg); +} - If a non-default argument follows a default argument, set an - exception and return -1. +static int +compiler_addop_name(struct compiler *c, int opcode, PyObject *dict, + PyObject *o) +{ + int arg; + PyObject *mangled = _Py_Mangle(c->u->u_private, o); + if (!mangled) + return 0; + arg = compiler_add_o(c, dict, mangled); + Py_DECREF(mangled); + if (arg < 0) + return 0; + return compiler_addop_i(c, opcode, arg); +} + +/* Add an opcode with an integer argument. + Returns 0 on failure, 1 on success. */ static int -com_argdefs(struct compiling *c, node *n) +compiler_addop_i(struct compiler *c, int opcode, int oparg) { - int i, nch, ndefs; - if (TYPE(n) == lambdef) { - /* lambdef: 'lambda' [varargslist] ':' test */ - n = CHILD(n, 1); - } - else { - REQ(n, funcdef); - /* funcdef: [decorators] 'def' NAME parameters ':' suite */ - n = RCHILD(n, -3); - REQ(n, parameters); /* parameters: '(' [varargslist] ')' */ - n = CHILD(n, 1); - } - if (TYPE(n) != varargslist) - return 0; - /* varargslist: - (fpdef ['=' test] ',')* '*' ....... | - fpdef ['=' test] (',' fpdef ['=' test])* [','] */ - nch = NCH(n); - ndefs = 0; - for (i = 0; i < nch; i++) { - int t; - if (TYPE(CHILD(n, i)) == STAR || - TYPE(CHILD(n, i)) == DOUBLESTAR) - break; - i++; - if (i >= nch) - t = RPAR; /* Anything except EQUAL or COMMA */ - else - t = TYPE(CHILD(n, i)); - if (t == EQUAL) { - i++; - ndefs++; - com_node(c, CHILD(n, i)); - i++; - if (i >= nch) - break; - t = TYPE(CHILD(n, i)); - } - else { - /* Treat "(a=1, b)" as an error */ - if (ndefs) { - com_error(c, PyExc_SyntaxError, - "non-default argument follows default argument"); - return -1; - } - } - if (t != COMMA) - break; - } - return ndefs; + struct instr *i; + int off; + off = compiler_next_instr(c, c->u->u_curblock); + if (off < 0) + return 0; + i = &c->u->u_curblock->b_instr[off]; + i->i_opcode = opcode; + i->i_oparg = oparg; + i->i_hasarg = 1; + compiler_set_lineno(c, off); + return 1; } -static void -com_decorator_name(struct compiling *c, node *n) +static int +compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) { - /* dotted_name: NAME ('.' NAME)* */ - - int i, nch; - node *varname; + struct instr *i; + int off; - REQ(n, dotted_name); - nch = NCH(n); - assert(nch >= 1 && nch % 2 == 1); + assert(b != NULL); + off = compiler_next_instr(c, c->u->u_curblock); + if (off < 0) + return 0; + compiler_set_lineno(c, off); + i = &c->u->u_curblock->b_instr[off]; + i->i_opcode = opcode; + i->i_target = b; + i->i_hasarg = 1; + if (absolute) + i->i_jabs = 1; + else + i->i_jrel = 1; + return 1; +} - varname = CHILD(n, 0); - REQ(varname, NAME); - com_addop_varname(c, VAR_LOAD, STR(varname)); - com_push(c, 1); - - for (i = 1; i < nch; i += 2) { - node *attrname; - - REQ(CHILD(n, i), DOT); +/* The distinction between NEW_BLOCK and NEXT_BLOCK is subtle. (I'd + like to find better names.) NEW_BLOCK() creates a new block and sets + it as the current block. NEXT_BLOCK() also creates an implicit jump + from the current block to the new block. +*/ - attrname = CHILD(n, i + 1); - REQ(attrname, NAME); - com_addop_name(c, LOAD_ATTR, STR(attrname)); - } +/* XXX The returns inside these macros make it impossible to decref + objects created in the local function. +*/ + + +#define NEW_BLOCK(C) { \ + if (compiler_use_new_block((C)) == NULL) \ + return 0; \ } -static void -com_decorator(struct compiling *c, node *n) -{ - /* decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE */ - int nch = NCH(n); - assert(nch >= 3); - REQ(CHILD(n, 0), AT); - REQ(RCHILD(n, -1), NEWLINE); - com_decorator_name(c, CHILD(n, 1)); - - if (nch > 3) { - assert(nch == 5 || nch == 6); - REQ(CHILD(n, 2), LPAR); - REQ(RCHILD(n, -2), RPAR); - com_call_function(c, CHILD(n, 3)); - } +#define NEXT_BLOCK(C) { \ + if (compiler_next_block((C)) == NULL) \ + return 0; \ } -static int -com_decorators(struct compiling *c, node *n) -{ - int i, nch; - - /* decorator+ */ - nch = NCH(n); - assert(nch >= 1); +#define ADDOP(C, OP) { \ + if (!compiler_addop((C), (OP))) \ + return 0; \ +} - for (i = 0; i < nch; ++i) { - node *ch = CHILD(n, i); - REQ(ch, decorator); +#define ADDOP_O(C, OP, O, TYPE) { \ + if (!compiler_addop_o((C), (OP), (C)->u->u_ ## TYPE, (O))) \ + return 0; \ +} - com_decorator(c, ch); - } +#define ADDOP_NAME(C, OP, O, TYPE) { \ + if (!compiler_addop_name((C), (OP), (C)->u->u_ ## TYPE, (O))) \ + return 0; \ +} - return nch; +#define ADDOP_I(C, OP, O) { \ + if (!compiler_addop_i((C), (OP), (O))) \ + return 0; \ } -static void -com_funcdef(struct compiling *c, node *n) -{ - PyObject *co; - int ndefs, ndecorators; - - REQ(n, funcdef); - /* -6 -5 -4 -3 -2 -1 - funcdef: [decorators] 'def' NAME parameters ':' suite */ +#define ADDOP_JABS(C, OP, O) { \ + if (!compiler_addop_j((C), (OP), (O), 1)) \ + return 0; \ +} - if (NCH(n) == 6) - ndecorators = com_decorators(c, CHILD(n, 0)); - else - ndecorators = 0; +#define ADDOP_JREL(C, OP, O) { \ + if (!compiler_addop_j((C), (OP), (O), 0)) \ + return 0; \ +} - ndefs = com_argdefs(c, n); - if (ndefs < 0) - return; - symtable_enter_scope(c->c_symtable, STR(RCHILD(n, -4)), TYPE(n), - n->n_lineno); - co = (PyObject *)icompile(n, c); - symtable_exit_scope(c->c_symtable); - if (co == NULL) - c->c_errors++; - else { - int closure = com_make_closure(c, (PyCodeObject *)co); - int i = com_addconst(c, co); - com_addoparg(c, LOAD_CONST, i); - com_push(c, 1); - if (closure) - com_addoparg(c, MAKE_CLOSURE, ndefs); - else - com_addoparg(c, MAKE_FUNCTION, ndefs); - com_pop(c, ndefs); +/* VISIT and VISIT_SEQ takes an ASDL type as their second argument. They use + the ASDL name to synthesize the name of the C type and the visit function. +*/ - while (ndecorators > 0) { - com_addoparg(c, CALL_FUNCTION, 1); - com_pop(c, 1); - --ndecorators; - } +#define VISIT(C, TYPE, V) {\ + if (!compiler_visit_ ## TYPE((C), (V))) \ + return 0; \ +} - com_addop_varname(c, VAR_STORE, STR(RCHILD(n, -4))); - com_pop(c, 1); - Py_DECREF(co); - } +#define VISIT_SLICE(C, V, CTX) {\ + if (!compiler_visit_slice((C), (V), (CTX))) \ + return 0; \ } -static void -com_bases(struct compiling *c, node *n) +#define VISIT_SEQ(C, TYPE, SEQ) { \ + int i; \ + asdl_seq *seq = (SEQ); /* avoid variable capture */ \ + for (i = 0; i < asdl_seq_LEN(seq); i++) { \ + TYPE ## _ty elt = asdl_seq_GET(seq, i); \ + if (!compiler_visit_ ## TYPE((C), elt)) \ + return 0; \ + } \ +} + +static int +compiler_isdocstring(stmt_ty s) { - int i; - REQ(n, testlist); - /* testlist: test (',' test)* [','] */ - for (i = 0; i < NCH(n); i += 2) - com_node(c, CHILD(n, i)); - i = (NCH(n)+1) / 2; - com_addoparg(c, BUILD_TUPLE, i); - com_pop(c, i-1); + if (s->kind != Expr_kind) + return 0; + return s->v.Expr.value->kind == Str_kind; } -static void -com_classdef(struct compiling *c, node *n) +/* Compile a sequence of statements, checking for a docstring. */ + +static int +compiler_body(struct compiler *c, asdl_seq *stmts) { - int i; - PyObject *v; - PyCodeObject *co; - char *name; + int i = 0; + stmt_ty st; - REQ(n, classdef); - /* classdef: class NAME ['(' [testlist] ')'] ':' suite */ - if ((v = PyString_InternFromString(STR(CHILD(n, 1)))) == NULL) { - c->c_errors++; - return; - } - /* Push the class name on the stack */ - i = com_addconst(c, v); - com_addoparg(c, LOAD_CONST, i); - com_push(c, 1); - Py_DECREF(v); - /* Push the tuple of base classes on the stack */ - if (TYPE(CHILD(n, 2)) != LPAR || - TYPE(CHILD(n, 3)) == RPAR) { - com_addoparg(c, BUILD_TUPLE, 0); - com_push(c, 1); - } - else - com_bases(c, CHILD(n, 3)); - name = STR(CHILD(n, 1)); - symtable_enter_scope(c->c_symtable, name, TYPE(n), n->n_lineno); - co = icompile(n, c); - symtable_exit_scope(c->c_symtable); - if (co == NULL) - c->c_errors++; - else { - int closure = com_make_closure(c, co); - i = com_addconst(c, (PyObject *)co); - com_addoparg(c, LOAD_CONST, i); - com_push(c, 1); - if (closure) { - com_addoparg(c, MAKE_CLOSURE, 0); - com_pop(c, PyCode_GetNumFree(co)); - } else - com_addoparg(c, MAKE_FUNCTION, 0); - com_addoparg(c, CALL_FUNCTION, 0); - com_addbyte(c, BUILD_CLASS); - com_pop(c, 2); - com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1))); - com_pop(c, 1); - Py_DECREF(co); + if (!asdl_seq_LEN(stmts)) + return 1; + st = asdl_seq_GET(stmts, 0); + if (compiler_isdocstring(st)) { + i = 1; + VISIT(c, expr, st->v.Expr.value); + if (!compiler_nameop(c, __doc__, Store)) + return 0; } + for (; i < asdl_seq_LEN(stmts); i++) + VISIT(c, stmt, asdl_seq_GET(stmts, i)); + return 1; } -static void -com_node(struct compiling *c, node *n) +static PyCodeObject * +compiler_mod(struct compiler *c, mod_ty mod) { - loop: - if (c->c_errors) - return; - switch (TYPE(n)) { - - /* Definition nodes */ - - case funcdef: - com_funcdef(c, n); - break; - case classdef: - com_classdef(c, n); - break; - - /* Trivial parse tree nodes */ - - case stmt: - case small_stmt: - case flow_stmt: - n = CHILD(n, 0); - goto loop; - - case simple_stmt: - /* small_stmt (';' small_stmt)* [';'] NEWLINE */ - com_set_lineno(c, n->n_lineno); - { - int i; - for (i = 0; i < NCH(n)-1; i += 2) - com_node(c, CHILD(n, i)); - } - break; - - case compound_stmt: - com_set_lineno(c, n->n_lineno); - n = CHILD(n, 0); - goto loop; - - /* Statement nodes */ - - case expr_stmt: - com_expr_stmt(c, n); - break; - case print_stmt: - com_print_stmt(c, n); - break; - case del_stmt: /* 'del' exprlist */ - com_assign(c, CHILD(n, 1), OP_DELETE, NULL); - break; - case pass_stmt: - break; - case break_stmt: - if (c->c_loops == 0) { - com_error(c, PyExc_SyntaxError, - "'break' outside loop"); - } - com_addbyte(c, BREAK_LOOP); - break; - case continue_stmt: - com_continue_stmt(c, n); - break; - case return_stmt: - com_return_stmt(c, n); - break; - case yield_stmt: - com_yield_stmt(c, n); - break; - case raise_stmt: - com_raise_stmt(c, n); - break; - case import_stmt: - com_import_stmt(c, n); - break; - case global_stmt: - break; - case exec_stmt: - com_exec_stmt(c, n); - break; - case assert_stmt: - com_assert_stmt(c, n); - break; - case if_stmt: - com_if_stmt(c, n); - break; - case while_stmt: - com_while_stmt(c, n); - break; - case for_stmt: - com_for_stmt(c, n); - break; - case try_stmt: - com_try_stmt(c, n); - break; - case suite: - com_suite(c, n); - break; - - /* Expression nodes */ - - case yield_expr: - com_yield_expr(c, n); - break; - - case testlist: - case testlist1: - case testlist_safe: - com_list(c, n, 0); - break; - case test: - com_test(c, n); - break; - case and_test: - com_and_test(c, n); - break; - case not_test: - com_not_test(c, n); - break; - case comparison: - com_comparison(c, n); - break; - case exprlist: - com_list(c, n, 0); - break; - case expr: - com_expr(c, n); - break; - case xor_expr: - com_xor_expr(c, n); - break; - case and_expr: - com_and_expr(c, n); - break; - case shift_expr: - com_shift_expr(c, n); - break; - case arith_expr: - com_arith_expr(c, n); - break; - case term: - com_term(c, n); + PyCodeObject *co; + int addNone = 1; + static PyObject *module; + if (!module) { + module = PyString_FromString(""); + if (!module) + return NULL; + } + if (!compiler_enter_scope(c, module, mod, 1)) + return NULL; + switch (mod->kind) { + case Module_kind: + if (!compiler_body(c, mod->v.Module.body)) + return 0; break; - case factor: - com_factor(c, n); + case Interactive_kind: + c->c_interactive = 1; + VISIT_SEQ(c, stmt, mod->v.Interactive.body); break; - case power: - com_power(c, n); + case Expression_kind: + VISIT(c, expr, mod->v.Expression.body); + addNone = 0; break; - case atom: - com_atom(c, n); + case Suite_kind: + assert(0); /* XXX: what should we do here? */ + VISIT_SEQ(c, stmt, mod->v.Suite.body); break; - - default: - com_error(c, PyExc_SystemError, - "com_node: unexpected node type"); + default: + assert(0); } + co = assemble(c, addNone); + compiler_exit_scope(c); + return co; } -static void com_fplist(struct compiling *, node *); +/* The test for LOCAL must come before the test for FREE in order to + handle classes where name is both local and free. The local var is + a method and the free var is a free var referenced within a method. +*/ -static void -com_fpdef(struct compiling *c, node *n) -{ - REQ(n, fpdef); /* fpdef: NAME | '(' fplist ')' */ - if (TYPE(CHILD(n, 0)) == LPAR) - com_fplist(c, CHILD(n, 1)); - else { - com_addop_varname(c, VAR_STORE, STR(CHILD(n, 0))); - com_pop(c, 1); - } +static int +get_ref_type(struct compiler *c, PyObject *name) +{ + int scope = PyST_GetScope(c->u->u_ste, name); + if (scope == 0) { + char buf[350]; + PyOS_snprintf(buf, sizeof(buf), + "unknown scope for %.100s in %.100s(%s) in %s\n" + "symbols: %s\nlocals: %s\nglobals: %s\n", + PyString_AS_STRING(name), + PyString_AS_STRING(c->u->u_name), + PyObject_REPR(c->u->u_ste->ste_id), + c->c_filename, + PyObject_REPR(c->u->u_ste->ste_symbols), + PyObject_REPR(c->u->u_varnames), + PyObject_REPR(c->u->u_names) + ); + Py_FatalError(buf); + } + + return scope; } -static void -com_fplist(struct compiling *c, node *n) +static int +compiler_lookup_arg(PyObject *dict, PyObject *name) { - REQ(n, fplist); /* fplist: fpdef (',' fpdef)* [','] */ - if (NCH(n) == 1) { - com_fpdef(c, CHILD(n, 0)); - } - else { - int i = (NCH(n)+1)/2; - com_addoparg(c, UNPACK_SEQUENCE, i); - com_push(c, i-1); - for (i = 0; i < NCH(n); i += 2) - com_fpdef(c, CHILD(n, i)); - } + PyObject *k, *v; + k = Py_BuildValue("(OO)", name, name->ob_type); + if (k == NULL) + return -1; + v = PyDict_GetItem(dict, k); + if (v == NULL) + return -1; + return PyInt_AS_LONG(v); } -static void -com_arglist(struct compiling *c, node *n) +static int +compiler_make_closure(struct compiler *c, PyCodeObject *co, int args) { - int nch, i, narg; - int complex = 0; - char nbuf[30]; - REQ(n, varargslist); - /* varargslist: - (fpdef ['=' test] ',')* (fpdef ['=' test] | '*' .....) */ - nch = NCH(n); - /* Enter all arguments in table of locals */ - for (i = 0, narg = 0; i < nch; i++) { - node *ch = CHILD(n, i); - node *fp; - if (TYPE(ch) == STAR || TYPE(ch) == DOUBLESTAR) - break; - REQ(ch, fpdef); /* fpdef: NAME | '(' fplist ')' */ - fp = CHILD(ch, 0); - if (TYPE(fp) != NAME) { - PyOS_snprintf(nbuf, sizeof(nbuf), ".%d", i); - complex = 1; - } - narg++; - /* all name updates handled by symtable */ - if (++i >= nch) - break; - ch = CHILD(n, i); - if (TYPE(ch) == EQUAL) - i += 2; - else - REQ(ch, COMMA); - } - if (complex) { - /* Generate code for complex arguments only after - having counted the simple arguments */ - int ilocal = 0; - for (i = 0; i < nch; i++) { - node *ch = CHILD(n, i); - node *fp; - if (TYPE(ch) == STAR || TYPE(ch) == DOUBLESTAR) - break; - REQ(ch, fpdef); /* fpdef: NAME | '(' fplist ')' */ - fp = CHILD(ch, 0); - if (TYPE(fp) != NAME) { - com_addoparg(c, LOAD_FAST, ilocal); - com_push(c, 1); - com_fpdef(c, ch); - } - ilocal++; - if (++i >= nch) - break; - ch = CHILD(n, i); - if (TYPE(ch) == EQUAL) - i += 2; - else - REQ(ch, COMMA); + int i, free = PyCode_GetNumFree(co); + if (free == 0) { + ADDOP_O(c, LOAD_CONST, (PyObject*)co, consts); + ADDOP_I(c, MAKE_FUNCTION, args); + return 1; + } + for (i = 0; i < free; ++i) { + /* Bypass com_addop_varname because it will generate + LOAD_DEREF but LOAD_CLOSURE is needed. + */ + PyObject *name = PyTuple_GET_ITEM(co->co_freevars, i); + int arg, reftype; + + /* Special case: If a class contains a method with a + free variable that has the same name as a method, + the name will be considered free *and* local in the + class. It should be handled by the closure, as + well as by the normal name loookup logic. + */ + reftype = get_ref_type(c, name); + if (reftype == CELL) + arg = compiler_lookup_arg(c->u->u_cellvars, name); + else /* (reftype == FREE) */ + arg = compiler_lookup_arg(c->u->u_freevars, name); + if (arg == -1) { + printf("lookup %s in %s %d %d\n" + "freevars of %s: %s\n", + PyObject_REPR(name), + PyString_AS_STRING(c->u->u_name), + reftype, arg, + PyString_AS_STRING(co->co_name), + PyObject_REPR(co->co_freevars)); + Py_FatalError("compiler_make_closure()"); } + ADDOP_I(c, LOAD_CLOSURE, arg); } + ADDOP_I(c, BUILD_TUPLE, free); + ADDOP_O(c, LOAD_CONST, (PyObject*)co, consts); + ADDOP_I(c, MAKE_CLOSURE, args); + return 1; } -static void -com_file_input(struct compiling *c, node *n) +static int +compiler_decorators(struct compiler *c, asdl_seq* decos) { int i; - PyObject *doc; - REQ(n, file_input); /* (NEWLINE | stmt)* ENDMARKER */ - doc = get_docstring(c, n); - if (doc != NULL) { - int i = com_addconst(c, doc); - Py_DECREF(doc); - com_addoparg(c, LOAD_CONST, i); - com_push(c, 1); - com_addop_name(c, STORE_NAME, "__doc__"); - com_pop(c, 1); - } - for (i = 0; i < NCH(n); i++) { - node *ch = CHILD(n, i); - if (TYPE(ch) != ENDMARKER && TYPE(ch) != NEWLINE) - com_node(c, ch); - } -} -/* Top-level compile-node interface */ + if (!decos) + return 1; -static void -compile_funcdef(struct compiling *c, node *n) -{ - PyObject *doc; - node *ch; - REQ(n, funcdef); - /* -6 -5 -4 -3 -2 -1 - funcdef: [decorators] 'def' NAME parameters ':' suite */ - c->c_name = STR(RCHILD(n, -4)); - doc = get_docstring(c, RCHILD(n, -1)); - if (doc != NULL) { - (void) com_addconst(c, doc); - Py_DECREF(doc); + for (i = 0; i < asdl_seq_LEN(decos); i++) { + VISIT(c, expr, asdl_seq_GET(decos, i)); } - else - (void) com_addconst(c, Py_None); /* No docstring */ - ch = RCHILD(n, -3); /* parameters: '(' [varargslist] ')' */ - ch = CHILD(ch, 1); /* ')' | varargslist */ - if (TYPE(ch) == varargslist) - com_arglist(c, ch); - c->c_infunction = 1; - com_node(c, RCHILD(n, -1)); - c->c_infunction = 0; - com_strip_lnotab(c); - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - com_addbyte(c, RETURN_VALUE); - com_pop(c, 1); + return 1; } -static void -compile_lambdef(struct compiling *c, node *n) +static int +compiler_arguments(struct compiler *c, arguments_ty args) { - node *ch; - REQ(n, lambdef); /* lambdef: 'lambda' [varargslist] ':' test */ - c->c_name = ""; - - ch = CHILD(n, 1); - (void) com_addconst(c, Py_None); /* No docstring */ - if (TYPE(ch) == varargslist) { - com_arglist(c, ch); - ch = CHILD(n, 3); + int i; + int n = asdl_seq_LEN(args->args); + /* Correctly handle nested argument lists */ + for (i = 0; i < n; i++) { + expr_ty arg = asdl_seq_GET(args->args, i); + if (arg->kind == Tuple_kind) { + PyObject *id = PyString_FromFormat(".%d", i); + if (id == NULL) { + return 0; + } + if (!compiler_nameop(c, id, Load)) { + Py_DECREF(id); + return 0; + } + Py_DECREF(id); + VISIT(c, expr, arg); + } } - else - ch = CHILD(n, 2); - com_node(c, ch); - com_addbyte(c, RETURN_VALUE); - com_pop(c, 1); + return 1; } -static void -compile_classdef(struct compiling *c, node *n) +static int +compiler_function(struct compiler *c, stmt_ty s) { - node *ch; - PyObject *doc; - REQ(n, classdef); - /* classdef: 'class' NAME ['(' testlist ')'] ':' suite */ - c->c_name = STR(CHILD(n, 1)); - c->c_private = c->c_name; - /* Initialize local __module__ from global __name__ */ - com_addop_name(c, LOAD_GLOBAL, "__name__"); - com_addop_name(c, STORE_NAME, "__module__"); - ch = CHILD(n, NCH(n)-1); /* The suite */ - doc = get_docstring(c, ch); - if (doc != NULL) { - int i = com_addconst(c, doc); - Py_DECREF(doc); - com_addoparg(c, LOAD_CONST, i); - com_push(c, 1); - com_addop_name(c, STORE_NAME, "__doc__"); - com_pop(c, 1); + PyCodeObject *co; + PyObject *first_const = Py_None; + arguments_ty args = s->v.FunctionDef.args; + asdl_seq* decos = s->v.FunctionDef.decorators; + stmt_ty st; + int i, n, docstring; + + assert(s->kind == FunctionDef_kind); + + if (!compiler_decorators(c, decos)) + return 0; + if (args->defaults) + VISIT_SEQ(c, expr, args->defaults); + if (!compiler_enter_scope(c, s->v.FunctionDef.name, (void *)s, + s->lineno)) + return 0; + + st = asdl_seq_GET(s->v.FunctionDef.body, 0); + docstring = compiler_isdocstring(st); + if (docstring) + first_const = st->v.Expr.value->v.Str.s; + if (compiler_add_o(c, c->u->u_consts, first_const) < 0) + return 0; + + /* unpack nested arguments */ + compiler_arguments(c, args); + + c->u->u_argcount = asdl_seq_LEN(args->args); + n = asdl_seq_LEN(s->v.FunctionDef.body); + /* if there was a docstring, we need to skip the first statement */ + for (i = docstring; i < n; i++) { + stmt_ty s2 = asdl_seq_GET(s->v.FunctionDef.body, i); + if (i == 0 && s2->kind == Expr_kind && + s2->v.Expr.value->kind == Str_kind) + continue; + VISIT(c, stmt, s2); } - else - (void) com_addconst(c, Py_None); - com_node(c, ch); - com_strip_lnotab(c); - com_addbyte(c, LOAD_LOCALS); - com_push(c, 1); - com_addbyte(c, RETURN_VALUE); - com_pop(c, 1); -} + co = assemble(c, 1); + if (co == NULL) + return 0; + compiler_exit_scope(c); -static void -compile_generator_expression(struct compiling *c, node *n) -{ - /* testlist_gexp: test gen_for */ - /* argument: test gen_for */ - REQ(CHILD(n, 0), test); - REQ(CHILD(n, 1), gen_for); - - c->c_name = ""; - c->c_infunction = 1; - com_gen_for(c, CHILD(n, 1), CHILD(n, 0), 1); - c->c_infunction = 0; - - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - com_addbyte(c, RETURN_VALUE); - com_pop(c, 1); + compiler_make_closure(c, co, asdl_seq_LEN(args->defaults)); + + for (i = 0; i < asdl_seq_LEN(decos); i++) { + ADDOP_I(c, CALL_FUNCTION, 1); + } + + return compiler_nameop(c, s->v.FunctionDef.name, Store); } -static void -compile_node(struct compiling *c, node *n) +static int +compiler_class(struct compiler *c, stmt_ty s) { - com_set_lineno(c, n->n_lineno); - - switch (TYPE(n)) { - - case single_input: /* One interactive command */ - /* NEWLINE | simple_stmt | compound_stmt NEWLINE */ - c->c_interactive++; - n = CHILD(n, 0); - if (TYPE(n) != NEWLINE) - com_node(c, n); - com_strip_lnotab(c); - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - com_addbyte(c, RETURN_VALUE); - com_pop(c, 1); - c->c_interactive--; - break; - - case file_input: /* A whole file, or built-in function exec() */ - com_file_input(c, n); - com_strip_lnotab(c); - com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); - com_push(c, 1); - com_addbyte(c, RETURN_VALUE); - com_pop(c, 1); - break; - - case eval_input: /* Built-in function input() */ - com_node(c, CHILD(n, 0)); - com_addbyte(c, RETURN_VALUE); - com_pop(c, 1); - break; - - case lambdef: /* anonymous function definition */ - compile_lambdef(c, n); - break; + int n; + PyCodeObject *co; + PyObject *str; + /* push class name on stack, needed by BUILD_CLASS */ + ADDOP_O(c, LOAD_CONST, s->v.ClassDef.name, consts); + /* push the tuple of base classes on the stack */ + n = asdl_seq_LEN(s->v.ClassDef.bases); + if (n > 0) + VISIT_SEQ(c, expr, s->v.ClassDef.bases); + ADDOP_I(c, BUILD_TUPLE, n); + if (!compiler_enter_scope(c, s->v.ClassDef.name, (void *)s, + s->lineno)) + return 0; + c->u->u_private = s->v.ClassDef.name; + Py_INCREF(c->u->u_private); + str = PyString_InternFromString("__name__"); + if (!str || !compiler_nameop(c, str, Load)) { + Py_XDECREF(str); + return 0; + } + + Py_DECREF(str); + str = PyString_InternFromString("__module__"); + if (!str || !compiler_nameop(c, str, Store)) { + Py_XDECREF(str); + return 0; + } + Py_DECREF(str); + + if (!compiler_body(c, s->v.ClassDef.body)) + return 0; - case funcdef: /* A function definition */ - compile_funcdef(c, n); - break; - - case classdef: /* A class definition */ - compile_classdef(c, n); - break; - - case testlist_gexp: /* A generator expression */ - case argument: /* A generator expression */ - compile_generator_expression(c, n); - break; + ADDOP(c, LOAD_LOCALS); + ADDOP(c, RETURN_VALUE); + co = assemble(c, 1); + if (co == NULL) + return 0; + compiler_exit_scope(c); - default: - com_error(c, PyExc_SystemError, - "compile_node: unexpected node type"); - } + compiler_make_closure(c, co, 0); + ADDOP_I(c, CALL_FUNCTION, 0); + ADDOP(c, BUILD_CLASS); + if (!compiler_nameop(c, s->v.ClassDef.name, Store)) + return 0; + return 1; } -static PyObject * -dict_keys_inorder(PyObject *dict, int offset) +static int +compiler_lambda(struct compiler *c, expr_ty e) { - PyObject *tuple, *k, *v; - int i, pos = 0, size = PyDict_Size(dict); + PyCodeObject *co; + identifier name; + arguments_ty args = e->v.Lambda.args; + assert(e->kind == Lambda_kind); - tuple = PyTuple_New(size); - if (tuple == NULL) - return NULL; - while (PyDict_Next(dict, &pos, &k, &v)) { - i = PyInt_AS_LONG(v); - Py_INCREF(k); - assert((i - offset) < size); - PyTuple_SET_ITEM(tuple, i - offset, k); - } - return tuple; -} + name = PyString_InternFromString("lambda"); + if (!name) + return 0; -PyCodeObject * -PyNode_Compile(node *n, const char *filename) -{ - return PyNode_CompileFlags(n, filename, NULL); + if (args->defaults) + VISIT_SEQ(c, expr, args->defaults); + if (!compiler_enter_scope(c, name, (void *)e, e->lineno)) + return 0; + + /* unpack nested arguments */ + compiler_arguments(c, args); + + c->u->u_argcount = asdl_seq_LEN(args->args); + VISIT(c, expr, e->v.Lambda.body); + ADDOP(c, RETURN_VALUE); + co = assemble(c, 1); + if (co == NULL) + return 0; + compiler_exit_scope(c); + + compiler_make_closure(c, co, asdl_seq_LEN(args->defaults)); + Py_DECREF(name); + + return 1; } -PyCodeObject * -PyNode_CompileFlags(node *n, const char *filename, PyCompilerFlags *flags) -{ - return jcompile(n, filename, NULL, flags); +static int +compiler_print(struct compiler *c, stmt_ty s) +{ + int i, n; + bool dest; + + assert(s->kind == Print_kind); + n = asdl_seq_LEN(s->v.Print.values); + dest = false; + if (s->v.Print.dest) { + VISIT(c, expr, s->v.Print.dest); + dest = true; + } + for (i = 0; i < n; i++) { + expr_ty e = (expr_ty)asdl_seq_GET(s->v.Print.values, i); + if (dest) { + ADDOP(c, DUP_TOP); + VISIT(c, expr, e); + ADDOP(c, ROT_TWO); + ADDOP(c, PRINT_ITEM_TO); + } + else { + VISIT(c, expr, e); + ADDOP(c, PRINT_ITEM); + } + } + if (s->v.Print.nl) { + if (dest) + ADDOP(c, PRINT_NEWLINE_TO) + else + ADDOP(c, PRINT_NEWLINE) + } + else if (dest) + ADDOP(c, POP_TOP); + return 1; } -struct symtable * -PyNode_CompileSymtable(node *n, const char *filename) +static int +compiler_if(struct compiler *c, stmt_ty s) { - struct symtable *st; - PyFutureFeatures *ff; + basicblock *end, *next; - ff = PyNode_Future(n, filename); - if (ff == NULL) - return NULL; - st = symtable_build(n, ff, filename); - if (st == NULL) { - PyObject_FREE((void *)ff); - return NULL; - } - return st; + assert(s->kind == If_kind); + end = compiler_new_block(c); + if (end == NULL) + return 0; + next = compiler_new_block(c); + if (next == NULL) + return 0; + VISIT(c, expr, s->v.If.test); + ADDOP_JREL(c, JUMP_IF_FALSE, next); + ADDOP(c, POP_TOP); + VISIT_SEQ(c, stmt, s->v.If.body); + ADDOP_JREL(c, JUMP_FORWARD, end); + compiler_use_next_block(c, next); + ADDOP(c, POP_TOP); + if (s->v.If.orelse) + VISIT_SEQ(c, stmt, s->v.If.orelse); + compiler_use_next_block(c, end); + return 1; } -static PyCodeObject * -icompile(node *n, struct compiling *base) +static int +compiler_for(struct compiler *c, stmt_ty s) { - return jcompile(n, base->c_filename, base, NULL); + basicblock *start, *cleanup, *end; + + start = compiler_new_block(c); + cleanup = compiler_new_block(c); + end = compiler_new_block(c); + if (start == NULL || end == NULL || cleanup == NULL) + return 0; + ADDOP_JREL(c, SETUP_LOOP, end); + if (!compiler_push_fblock(c, LOOP, start)) + return 0; + VISIT(c, expr, s->v.For.iter); + ADDOP(c, GET_ITER); + compiler_use_next_block(c, start); + ADDOP_JREL(c, FOR_ITER, cleanup); + VISIT(c, expr, s->v.For.target); + VISIT_SEQ(c, stmt, s->v.For.body); + ADDOP_JABS(c, JUMP_ABSOLUTE, start); + compiler_use_next_block(c, cleanup); + ADDOP(c, POP_BLOCK); + compiler_pop_fblock(c, LOOP, start); + VISIT_SEQ(c, stmt, s->v.For.orelse); + compiler_use_next_block(c, end); + return 1; } -static PyCodeObject * -jcompile(node *n, const char *filename, struct compiling *base, - PyCompilerFlags *flags) +static int +compiler_while(struct compiler *c, stmt_ty s) { - struct compiling sc; - PyCodeObject *co; - if (!com_init(&sc, filename)) - return NULL; - if (flags && flags->cf_flags & PyCF_SOURCE_IS_UTF8) { - sc.c_encoding = "utf-8"; - } else if (TYPE(n) == encoding_decl) { - sc.c_encoding = STR(n); - n = CHILD(n, 0); - } else { - sc.c_encoding = NULL; - } - if (base) { - sc.c_private = base->c_private; - sc.c_symtable = base->c_symtable; - /* c_symtable still points to parent's symbols */ - if (base->c_nested - || (sc.c_symtable->st_cur->ste_type == TYPE_FUNCTION)) - sc.c_nested = 1; - sc.c_flags |= base->c_flags & PyCF_MASK; - if (base->c_encoding != NULL) { - assert(sc.c_encoding == NULL); - sc.c_encoding = base->c_encoding; - } - } else { - sc.c_private = NULL; - sc.c_future = PyNode_Future(n, filename); - if (sc.c_future == NULL) { - com_free(&sc); - return NULL; - } - if (flags) { - int merged = sc.c_future->ff_features | - flags->cf_flags; - sc.c_future->ff_features = merged; - flags->cf_flags = merged; - } - sc.c_symtable = symtable_build(n, sc.c_future, sc.c_filename); - if (sc.c_symtable == NULL) { - com_free(&sc); - return NULL; - } - /* reset symbol table for second pass */ - sc.c_symtable->st_nscopes = 1; - sc.c_symtable->st_pass = 2; - } - co = NULL; - if (symtable_load_symbols(&sc) < 0) { - sc.c_errors++; - goto exit; + basicblock *loop, *orelse, *end, *anchor = NULL; + int constant = expr_constant(s->v.While.test); + + if (constant == 0) + return 1; + loop = compiler_new_block(c); + end = compiler_new_block(c); + if (constant == -1) { + anchor = compiler_new_block(c); + if (anchor == NULL) + return 0; } - compile_node(&sc, n); - com_done(&sc); - if (sc.c_errors == 0) { - PyObject *consts, *names, *varnames, *filename, *name, - *freevars, *cellvars, *code; - names = PyList_AsTuple(sc.c_names); - varnames = PyList_AsTuple(sc.c_varnames); - cellvars = dict_keys_inorder(sc.c_cellvars, 0); - freevars = dict_keys_inorder(sc.c_freevars, - PyTuple_GET_SIZE(cellvars)); - filename = PyString_InternFromString(sc.c_filename); - name = PyString_InternFromString(sc.c_name); - code = optimize_code(sc.c_code, sc.c_consts, names, sc.c_lnotab); - consts = PyList_AsTuple(sc.c_consts); - if (!PyErr_Occurred()) - co = PyCode_New(sc.c_argcount, - sc.c_nlocals, - sc.c_maxstacklevel, - sc.c_flags, - code, - consts, - names, - varnames, - freevars, - cellvars, - filename, - name, - sc.c_firstlineno, - sc.c_lnotab); - Py_XDECREF(consts); - Py_XDECREF(names); - Py_XDECREF(varnames); - Py_XDECREF(freevars); - Py_XDECREF(cellvars); - Py_XDECREF(filename); - Py_XDECREF(name); - Py_XDECREF(code); + if (loop == NULL || end == NULL) + return 0; + if (s->v.While.orelse) { + orelse = compiler_new_block(c); + if (orelse == NULL) + return 0; } - else if (!PyErr_Occurred()) { - /* This could happen if someone called PyErr_Clear() after an - error was reported above. That's not supposed to happen, - but I just plugged one case and I'm not sure there can't be - others. In that case, raise SystemError so that at least - it gets reported instead dumping core. */ - PyErr_SetString(PyExc_SystemError, "lost syntax error"); + else + orelse = NULL; + + ADDOP_JREL(c, SETUP_LOOP, end); + compiler_use_next_block(c, loop); + if (!compiler_push_fblock(c, LOOP, loop)) + return 0; + if (constant == -1) { + VISIT(c, expr, s->v.While.test); + ADDOP_JREL(c, JUMP_IF_FALSE, anchor); + ADDOP(c, POP_TOP); } - exit: - if (base == NULL) { - PySymtable_Free(sc.c_symtable); - sc.c_symtable = NULL; + VISIT_SEQ(c, stmt, s->v.While.body); + ADDOP_JABS(c, JUMP_ABSOLUTE, loop); + + /* XXX should the two POP instructions be in a separate block + if there is no else clause ? + */ + + if (constant == -1) { + compiler_use_next_block(c, anchor); + ADDOP(c, POP_TOP); + ADDOP(c, POP_BLOCK); } - com_free(&sc); - return co; + compiler_pop_fblock(c, LOOP, loop); + if (orelse != NULL) + VISIT_SEQ(c, stmt, s->v.While.orelse); + compiler_use_next_block(c, end); + + return 1; } -int -PyCode_Addr2Line(PyCodeObject *co, int addrq) +static int +compiler_continue(struct compiler *c) { - int size = PyString_Size(co->co_lnotab) / 2; - unsigned char *p = (unsigned char*)PyString_AsString(co->co_lnotab); - int line = co->co_firstlineno; - int addr = 0; - while (--size >= 0) { - addr += *p++; - if (addr > addrq) - break; - line += *p++; + static const char LOOP_ERROR_MSG[] = "'continue' not properly in loop"; + int i; + + if (!c->u->u_nfblocks) + return compiler_error(c, LOOP_ERROR_MSG); + i = c->u->u_nfblocks - 1; + switch (c->u->u_fblock[i].fb_type) { + case LOOP: + ADDOP_JABS(c, JUMP_ABSOLUTE, c->u->u_fblock[i].fb_block); + break; + case EXCEPT: + case FINALLY_TRY: + while (--i >= 0 && c->u->u_fblock[i].fb_type != LOOP) + ; + if (i == -1) + return compiler_error(c, LOOP_ERROR_MSG); + ADDOP_JABS(c, CONTINUE_LOOP, c->u->u_fblock[i].fb_block); + break; + case FINALLY_END: + return compiler_error(c, + "'continue' not supported inside 'finally' clause"); } - return line; + + return 1; } -/* The test for LOCAL must come before the test for FREE in order to - handle classes where name is both local and free. The local var is - a method and the free var is a free var referenced within a method. +/* Code generated for "try: finally: " is as follows: + + SETUP_FINALLY L + + POP_BLOCK + LOAD_CONST + L: + END_FINALLY + + The special instructions use the block stack. Each block + stack entry contains the instruction that created it (here + SETUP_FINALLY), the level of the value stack at the time the + block stack entry was created, and a label (here L). + + SETUP_FINALLY: + Pushes the current value stack level and the label + onto the block stack. + POP_BLOCK: + Pops en entry from the block stack, and pops the value + stack until its level is the same as indicated on the + block stack. (The label is ignored.) + END_FINALLY: + Pops a variable number of entries from the *value* stack + and re-raises the exception they specify. The number of + entries popped depends on the (pseudo) exception type. + + The block stack is unwound when an exception is raised: + when a SETUP_FINALLY entry is found, the exception is pushed + onto the value stack (and the exception condition is cleared), + and the interpreter jumps to the label gotten from the block + stack. */ static int -get_ref_type(struct compiling *c, char *name) +compiler_try_finally(struct compiler *c, stmt_ty s) { - char buf[350]; - PyObject *v; - - if (PyDict_GetItemString(c->c_cellvars, name) != NULL) - return CELL; - if (PyDict_GetItemString(c->c_locals, name) != NULL) - return LOCAL; - if (PyDict_GetItemString(c->c_freevars, name) != NULL) - return FREE; - v = PyDict_GetItemString(c->c_globals, name); - if (v) { - if (v == Py_None) - return GLOBAL_EXPLICIT; - else { - return GLOBAL_IMPLICIT; - } - } - PyOS_snprintf(buf, sizeof(buf), - "unknown scope for %.100s in %.100s(%s) " - "in %s\nsymbols: %s\nlocals: %s\nglobals: %s\n", - name, c->c_name, - PyObject_REPR(c->c_symtable->st_cur->ste_id), - c->c_filename, - PyObject_REPR(c->c_symtable->st_cur->ste_symbols), - PyObject_REPR(c->c_locals), - PyObject_REPR(c->c_globals) - ); + basicblock *body, *end; + body = compiler_new_block(c); + end = compiler_new_block(c); + if (body == NULL || end == NULL) + return 0; + + ADDOP_JREL(c, SETUP_FINALLY, end); + compiler_use_next_block(c, body); + if (!compiler_push_fblock(c, FINALLY_TRY, body)) + return 0; + VISIT_SEQ(c, stmt, s->v.TryFinally.body); + ADDOP(c, POP_BLOCK); + compiler_pop_fblock(c, FINALLY_TRY, body); - Py_FatalError(buf); - return -1; + ADDOP_O(c, LOAD_CONST, Py_None, consts); + compiler_use_next_block(c, end); + if (!compiler_push_fblock(c, FINALLY_END, end)) + return 0; + VISIT_SEQ(c, stmt, s->v.TryFinally.finalbody); + ADDOP(c, END_FINALLY); + compiler_pop_fblock(c, FINALLY_END, end); + + return 1; } -/* Helper functions to issue warnings */ +/* + Code generated for "try: S except E1, V1: S1 except E2, V2: S2 ...": + (The contents of the value stack is shown in [], with the top + at the right; 'tb' is trace-back info, 'val' the exception's + associated value, and 'exc' the exception.) + + Value stack Label Instruction Argument + [] SETUP_EXCEPT L1 + [] + [] POP_BLOCK + [] JUMP_FORWARD L0 + + [tb, val, exc] L1: DUP ) + [tb, val, exc, exc] ) + [tb, val, exc, exc, E1] COMPARE_OP EXC_MATCH ) only if E1 + [tb, val, exc, 1-or-0] JUMP_IF_FALSE L2 ) + [tb, val, exc, 1] POP ) + [tb, val, exc] POP + [tb, val] (or POP if no V1) + [tb] POP + [] + JUMP_FORWARD L0 + + [tb, val, exc, 0] L2: POP + [tb, val, exc] DUP + .............................etc....................... + [tb, val, exc, 0] Ln+1: POP + [tb, val, exc] END_FINALLY # re-raise exception + + [] L0: + + Of course, parts are not generated if Vi or Ei is not present. +*/ static int -issue_warning(const char *msg, const char *filename, int lineno) +compiler_try_except(struct compiler *c, stmt_ty s) { - if (PyErr_Occurred()) { - /* This can happen because symtable_node continues - processing even after raising a SyntaxError. - Calling PyErr_WarnExplicit now would clobber the - pending exception; instead we fail and let that - exception propagate. - */ - return -1; - } - if (PyErr_WarnExplicit(PyExc_SyntaxWarning, msg, filename, - lineno, NULL, NULL) < 0) { - if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) { - PyErr_SetString(PyExc_SyntaxError, msg); - PyErr_SyntaxLocation(filename, lineno); + basicblock *body, *orelse, *except, *end; + int i, n; + + body = compiler_new_block(c); + except = compiler_new_block(c); + orelse = compiler_new_block(c); + end = compiler_new_block(c); + if (body == NULL || except == NULL || orelse == NULL || end == NULL) + return 0; + ADDOP_JREL(c, SETUP_EXCEPT, except); + compiler_use_next_block(c, body); + if (!compiler_push_fblock(c, EXCEPT, body)) + return 0; + VISIT_SEQ(c, stmt, s->v.TryExcept.body); + ADDOP(c, POP_BLOCK); + compiler_pop_fblock(c, EXCEPT, body); + ADDOP_JREL(c, JUMP_FORWARD, orelse); + n = asdl_seq_LEN(s->v.TryExcept.handlers); + compiler_use_next_block(c, except); + for (i = 0; i < n; i++) { + excepthandler_ty handler = asdl_seq_GET( + s->v.TryExcept.handlers, i); + if (!handler->type && i < n-1) + return compiler_error(c, "default 'except:' must be last"); + except = compiler_new_block(c); + if (except == NULL) + return 0; + if (handler->type) { + ADDOP(c, DUP_TOP); + VISIT(c, expr, handler->type); + ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH); + ADDOP_JREL(c, JUMP_IF_FALSE, except); + ADDOP(c, POP_TOP); } - return -1; - } - return 0; + ADDOP(c, POP_TOP); + if (handler->name) { + VISIT(c, expr, handler->name); + } + else { + ADDOP(c, POP_TOP); + } + ADDOP(c, POP_TOP); + VISIT_SEQ(c, stmt, handler->body); + ADDOP_JREL(c, JUMP_FORWARD, end); + compiler_use_next_block(c, except); + if (handler->type) + ADDOP(c, POP_TOP); + } + ADDOP(c, END_FINALLY); + compiler_use_next_block(c, orelse); + VISIT_SEQ(c, stmt, s->v.TryExcept.orelse); + compiler_use_next_block(c, end); + return 1; } static int -symtable_warn(struct symtable *st, char *msg) +compiler_import_as(struct compiler *c, identifier name, identifier asname) { - if (issue_warning(msg, st->st_filename, st->st_cur->ste_lineno) < 0) { - st->st_errors++; - return -1; + /* The IMPORT_NAME opcode was already generated. This function + merely needs to bind the result to a name. + + If there is a dot in name, we need to split it and emit a + LOAD_ATTR for each name. + */ + const char *src = PyString_AS_STRING(name); + const char *dot = strchr(src, '.'); + if (dot) { + /* Consume the base module name to get the first attribute */ + src = dot + 1; + while (dot) { + /* NB src is only defined when dot != NULL */ + dot = strchr(src, '.'); + PyObject *attr = PyString_FromStringAndSize(src, + dot ? dot - src : strlen(src)); + ADDOP_O(c, LOAD_ATTR, attr, names); + src = dot + 1; + } } - return 0; + return compiler_nameop(c, asname, Store); } -/* Helper function for setting lineno and filename */ - -static struct symtable * -symtable_build(node *n, PyFutureFeatures *ff, const char *filename) -{ - struct symtable *st; - - st = symtable_init(); - if (st == NULL) - return NULL; - st->st_future = ff; - st->st_filename = filename; - symtable_enter_scope(st, TOP, TYPE(n), n->n_lineno); - if (st->st_errors > 0) - goto fail; - symtable_node(st, n); - if (st->st_errors > 0) - goto fail; - return st; - fail: - if (!PyErr_Occurred()) { - /* This could happen because after a syntax error is - detected, the symbol-table-building continues for - a while, and PyErr_Clear() might erroneously be - called during that process. One such case has been - fixed, but there might be more (now or later). - */ - PyErr_SetString(PyExc_SystemError, "lost exception"); +static int +compiler_import(struct compiler *c, stmt_ty s) +{ + /* The Import node stores a module name like a.b.c as a single + string. This is convenient for all cases except + import a.b.c as d + where we need to parse that string to extract the individual + module names. + XXX Perhaps change the representation to make this case simpler? + */ + int i, n = asdl_seq_LEN(s->v.Import.names); + for (i = 0; i < n; i++) { + alias_ty alias = asdl_seq_GET(s->v.Import.names, i); + int r; + + ADDOP_O(c, LOAD_CONST, Py_None, consts); + ADDOP_NAME(c, IMPORT_NAME, alias->name, names); + + if (alias->asname) { + return compiler_import_as(c, + alias->name, alias->asname); + } + else { + identifier tmp = alias->name; + const char *base = PyString_AS_STRING(alias->name); + char *dot = strchr(base, '.'); + if (dot) + tmp = PyString_FromStringAndSize(base, + dot - base); + r = compiler_nameop(c, tmp, Store); + if (dot) { + Py_DECREF(tmp); + } + if (!r) + return r; + } } - st->st_future = NULL; - st->st_filename = NULL; - PySymtable_Free(st); - return NULL; + return 1; } static int -symtable_init_compiling_symbols(struct compiling *c) +compiler_from_import(struct compiler *c, stmt_ty s) { - PyObject *varnames; + int i, n = asdl_seq_LEN(s->v.ImportFrom.names); + int star = 0; - varnames = c->c_symtable->st_cur->ste_varnames; - if (varnames == NULL) { - varnames = PyList_New(0); - if (varnames == NULL) - return -1; - c->c_symtable->st_cur->ste_varnames = varnames; - Py_INCREF(varnames); - } else - Py_INCREF(varnames); - c->c_varnames = varnames; - - c->c_globals = PyDict_New(); - if (c->c_globals == NULL) - return -1; - c->c_freevars = PyDict_New(); - if (c->c_freevars == NULL) - return -1; - c->c_cellvars = PyDict_New(); - if (c->c_cellvars == NULL) - return -1; - return 0; -} + PyObject *names = PyTuple_New(n); + if (!names) + return 0; -struct symbol_info { - int si_nlocals; - int si_ncells; - int si_nfrees; - int si_nimplicit; -}; + /* build up the names */ + for (i = 0; i < n; i++) { + alias_ty alias = asdl_seq_GET(s->v.ImportFrom.names, i); + Py_INCREF(alias->name); + PyTuple_SET_ITEM(names, i, alias->name); + } -static void -symtable_init_info(struct symbol_info *si) -{ - si->si_nlocals = 0; - si->si_ncells = 0; - si->si_nfrees = 0; - si->si_nimplicit = 0; + if (s->lineno > c->c_future->ff_lineno) { + if (!strcmp(PyString_AS_STRING(s->v.ImportFrom.module), + "__future__")) { + Py_DECREF(names); + return compiler_error(c, + "from __future__ imports must occur " + "at the beginning of the file"); + + } + } + + ADDOP_O(c, LOAD_CONST, names, consts); + ADDOP_NAME(c, IMPORT_NAME, s->v.ImportFrom.module, names); + for (i = 0; i < n; i++) { + alias_ty alias = asdl_seq_GET(s->v.ImportFrom.names, i); + identifier store_name; + + if (i == 0 && *PyString_AS_STRING(alias->name) == '*') { + assert(n == 1); + ADDOP(c, IMPORT_STAR); + star = 1; + break; + } + + ADDOP_NAME(c, IMPORT_FROM, alias->name, names); + store_name = alias->name; + if (alias->asname) + store_name = alias->asname; + + if (!compiler_nameop(c, store_name, Store)) { + Py_DECREF(names); + return 0; + } + } + if (!star) + /* remove imported module */ + ADDOP(c, POP_TOP); + return 1; } static int -symtable_resolve_free(struct compiling *c, PyObject *name, int flags, - struct symbol_info *si) +compiler_assert(struct compiler *c, stmt_ty s) { - PyObject *dict, *v; + static PyObject *assertion_error = NULL; + basicblock *end; - /* Seperate logic for DEF_FREE. If it occurs in a function, - it indicates a local that we must allocate storage for (a - cell var). If it occurs in a class, then the class has a - method and a free variable with the same name. - */ - if (c->c_symtable->st_cur->ste_type == TYPE_FUNCTION) { - /* If it isn't declared locally, it can't be a cell. */ - if (!(flags & (DEF_LOCAL | DEF_PARAM))) - return 0; - v = PyInt_FromLong(si->si_ncells++); - dict = c->c_cellvars; - } else { - /* If it is free anyway, then there is no need to do - anything here. - */ - if (is_free(flags ^ DEF_FREE_CLASS) - || (flags == DEF_FREE_CLASS)) + if (Py_OptimizeFlag) + return 1; + if (assertion_error == NULL) { + assertion_error = PyString_FromString("AssertionError"); + if (assertion_error == NULL) return 0; - v = PyInt_FromLong(si->si_nfrees++); - dict = c->c_freevars; } - if (v == NULL) - return -1; - if (PyDict_SetItem(dict, name, v) < 0) { - Py_DECREF(v); - return -1; + VISIT(c, expr, s->v.Assert.test); + end = compiler_new_block(c); + if (end == NULL) + return 0; + ADDOP_JREL(c, JUMP_IF_TRUE, end); + ADDOP(c, POP_TOP); + ADDOP_O(c, LOAD_GLOBAL, assertion_error, names); + if (s->v.Assert.msg) { + VISIT(c, expr, s->v.Assert.msg); + ADDOP_I(c, RAISE_VARARGS, 2); } - Py_DECREF(v); - return 0; + else { + ADDOP_I(c, RAISE_VARARGS, 1); + } + compiler_use_block(c, end); + ADDOP(c, POP_TOP); + return 1; } -/* If a variable is a cell and an argument, make sure that appears in - co_cellvars before any variable to its right in varnames. -*/ - - static int -symtable_cellvar_offsets(PyObject **cellvars, int argcount, - PyObject *varnames, int flags) -{ - PyObject *v = NULL; - PyObject *w, *d, *list = NULL; - int i, pos; - - if (flags & CO_VARARGS) - argcount++; - if (flags & CO_VARKEYWORDS) - argcount++; - for (i = argcount; --i >= 0; ) { - v = PyList_GET_ITEM(varnames, i); - if (PyDict_GetItem(*cellvars, v)) { - if (list == NULL) { - list = PyList_New(1); - if (list == NULL) - return -1; - PyList_SET_ITEM(list, 0, v); - Py_INCREF(v); - } else { - if (PyList_Insert(list, 0, v) < 0) { - Py_DECREF(list); - return -1; +compiler_visit_stmt(struct compiler *c, stmt_ty s) +{ + int i, n; + + c->u->u_lineno = s->lineno; + c->u->u_lineno_set = false; + switch (s->kind) { + case FunctionDef_kind: + return compiler_function(c, s); + case ClassDef_kind: + return compiler_class(c, s); + case Return_kind: + if (c->u->u_ste->ste_type != FunctionBlock) + return compiler_error(c, "'return' outside function"); + if (s->v.Return.value) { + if (c->u->u_ste->ste_generator) { + return compiler_error(c, + "'return' with argument inside generator"); + } + VISIT(c, expr, s->v.Return.value); + } + else + ADDOP_O(c, LOAD_CONST, Py_None, consts); + ADDOP(c, RETURN_VALUE); + break; + case Delete_kind: + VISIT_SEQ(c, expr, s->v.Delete.targets) + break; + case Assign_kind: + n = asdl_seq_LEN(s->v.Assign.targets); + VISIT(c, expr, s->v.Assign.value); + for (i = 0; i < n; i++) { + if (i < n - 1) + ADDOP(c, DUP_TOP); + VISIT(c, expr, + (expr_ty)asdl_seq_GET(s->v.Assign.targets, i)); + } + break; + case AugAssign_kind: + return compiler_augassign(c, s); + case Print_kind: + return compiler_print(c, s); + case For_kind: + return compiler_for(c, s); + case While_kind: + return compiler_while(c, s); + case If_kind: + return compiler_if(c, s); + case Raise_kind: + n = 0; + if (s->v.Raise.type) { + VISIT(c, expr, s->v.Raise.type); + n++; + if (s->v.Raise.inst) { + VISIT(c, expr, s->v.Raise.inst); + n++; + if (s->v.Raise.tback) { + VISIT(c, expr, s->v.Raise.tback); + n++; } } } - } - if (list == NULL) - return 0; - - /* There are cellvars that are also arguments. Create a dict - to replace cellvars and put the args at the front. - */ - d = PyDict_New(); - if (d == NULL) - return -1; - for (i = PyList_GET_SIZE(list); --i >= 0; ) { - v = PyInt_FromLong(i); - if (v == NULL) - goto fail; - if (PyDict_SetItem(d, PyList_GET_ITEM(list, i), v) < 0) - goto fail; - if (PyDict_DelItem(*cellvars, PyList_GET_ITEM(list, i)) < 0) - goto fail; - Py_DECREF(v); - } - pos = 0; - i = PyList_GET_SIZE(list); - Py_DECREF(list); - while (PyDict_Next(*cellvars, &pos, &v, &w)) { - w = PyInt_FromLong(i++); /* don't care about the old key */ - if (w == NULL) - goto fail; - if (PyDict_SetItem(d, v, w) < 0) { - Py_DECREF(w); - v = NULL; - goto fail; + ADDOP_I(c, RAISE_VARARGS, n); + break; + case TryExcept_kind: + return compiler_try_except(c, s); + case TryFinally_kind: + return compiler_try_finally(c, s); + case Assert_kind: + return compiler_assert(c, s); + case Import_kind: + return compiler_import(c, s); + case ImportFrom_kind: + return compiler_from_import(c, s); + case Exec_kind: + VISIT(c, expr, s->v.Exec.body); + if (s->v.Exec.globals) { + VISIT(c, expr, s->v.Exec.globals); + if (s->v.Exec.locals) { + VISIT(c, expr, s->v.Exec.locals); + } else { + ADDOP(c, DUP_TOP); + } + } else { + ADDOP_O(c, LOAD_CONST, Py_None, consts); + ADDOP(c, DUP_TOP); + } + ADDOP(c, EXEC_STMT); + break; + case Global_kind: + break; + case Expr_kind: + VISIT(c, expr, s->v.Expr.value); + if (c->c_interactive && c->c_nestlevel <= 1) { + ADDOP(c, PRINT_EXPR); + } + else { + ADDOP(c, POP_TOP); } - Py_DECREF(w); + break; + case Pass_kind: + break; + case Break_kind: + if (!c->u->u_nfblocks) + return compiler_error(c, "'break' outside loop"); + ADDOP(c, BREAK_LOOP); + break; + case Continue_kind: + return compiler_continue(c); } - Py_DECREF(*cellvars); - *cellvars = d; return 1; - fail: - Py_DECREF(d); - Py_XDECREF(v); - return -1; } static int -symtable_freevar_offsets(PyObject *freevars, int offset) -{ - PyObject *name, *v; - int pos; - - /* The cell vars are the first elements of the closure, - followed by the free vars. Update the offsets in - c_freevars to account for number of cellvars. */ - pos = 0; - while (PyDict_Next(freevars, &pos, &name, &v)) { - int i = PyInt_AS_LONG(v) + offset; - PyObject *o = PyInt_FromLong(i); - if (o == NULL) - return -1; - if (PyDict_SetItem(freevars, name, o) < 0) { - Py_DECREF(o); - return -1; - } - Py_DECREF(o); +unaryop(unaryop_ty op) +{ + switch (op) { + case Invert: + return UNARY_INVERT; + case Not: + return UNARY_NOT; + case UAdd: + return UNARY_POSITIVE; + case USub: + return UNARY_NEGATIVE; } return 0; } static int -symtable_check_unoptimized(struct compiling *c, - PySymtableEntryObject *ste, - struct symbol_info *si) -{ - char buf[300]; - - if (!(si->si_ncells || si->si_nfrees || ste->ste_child_free - || (ste->ste_nested && si->si_nimplicit))) - return 0; - -#define ILLEGAL_CONTAINS "contains a nested function with free variables" - -#define ILLEGAL_IS "is a nested function" - -#define ILLEGAL_IMPORT_STAR \ -"import * is not allowed in function '%.100s' because it %s" - -#define ILLEGAL_BARE_EXEC \ -"unqualified exec is not allowed in function '%.100s' it %s" - -#define ILLEGAL_EXEC_AND_IMPORT_STAR \ -"function '%.100s' uses import * and bare exec, which are illegal " \ -"because it %s" - - /* XXX perhaps the linenos for these opt-breaking statements - should be stored so the exception can point to them. */ - - if (ste->ste_child_free) { - if (ste->ste_optimized == OPT_IMPORT_STAR) - PyOS_snprintf(buf, sizeof(buf), - ILLEGAL_IMPORT_STAR, - PyString_AS_STRING(ste->ste_name), - ILLEGAL_CONTAINS); - else if (ste->ste_optimized == (OPT_BARE_EXEC | OPT_EXEC)) - PyOS_snprintf(buf, sizeof(buf), - ILLEGAL_BARE_EXEC, - PyString_AS_STRING(ste->ste_name), - ILLEGAL_CONTAINS); - else { - PyOS_snprintf(buf, sizeof(buf), - ILLEGAL_EXEC_AND_IMPORT_STAR, - PyString_AS_STRING(ste->ste_name), - ILLEGAL_CONTAINS); - } - } else { - if (ste->ste_optimized == OPT_IMPORT_STAR) - PyOS_snprintf(buf, sizeof(buf), - ILLEGAL_IMPORT_STAR, - PyString_AS_STRING(ste->ste_name), - ILLEGAL_IS); - else if (ste->ste_optimized == (OPT_BARE_EXEC | OPT_EXEC)) - PyOS_snprintf(buf, sizeof(buf), - ILLEGAL_BARE_EXEC, - PyString_AS_STRING(ste->ste_name), - ILLEGAL_IS); - else { - PyOS_snprintf(buf, sizeof(buf), - ILLEGAL_EXEC_AND_IMPORT_STAR, - PyString_AS_STRING(ste->ste_name), - ILLEGAL_IS); - } +binop(struct compiler *c, operator_ty op) +{ + switch (op) { + case Add: + return BINARY_ADD; + case Sub: + return BINARY_SUBTRACT; + case Mult: + return BINARY_MULTIPLY; + case Div: + if (c->c_flags && c->c_flags->cf_flags & CO_FUTURE_DIVISION) + return BINARY_TRUE_DIVIDE; + else + return BINARY_DIVIDE; + case Mod: + return BINARY_MODULO; + case Pow: + return BINARY_POWER; + case LShift: + return BINARY_LSHIFT; + case RShift: + return BINARY_RSHIFT; + case BitOr: + return BINARY_OR; + case BitXor: + return BINARY_XOR; + case BitAnd: + return BINARY_AND; + case FloorDiv: + return BINARY_FLOOR_DIVIDE; } - - PyErr_SetString(PyExc_SyntaxError, buf); - PyErr_SyntaxLocation(c->c_symtable->st_filename, - ste->ste_opt_lineno); - return -1; + return 0; } static int -symtable_update_flags(struct compiling *c, PySymtableEntryObject *ste, - struct symbol_info *si) -{ - if (c->c_future) - c->c_flags |= c->c_future->ff_features; - if (ste->ste_generator) - c->c_flags |= CO_GENERATOR; - if (ste->ste_type != TYPE_MODULE) - c->c_flags |= CO_NEWLOCALS; - if (ste->ste_type == TYPE_FUNCTION) { - c->c_nlocals = si->si_nlocals; - if (ste->ste_optimized == 0) - c->c_flags |= CO_OPTIMIZED; - else if (ste->ste_optimized != OPT_EXEC) - return symtable_check_unoptimized(c, ste, si); +cmpop(cmpop_ty op) +{ + switch (op) { + case Eq: + return PyCmp_EQ; + case NotEq: + return PyCmp_NE; + case Lt: + return PyCmp_LT; + case LtE: + return PyCmp_LE; + case Gt: + return PyCmp_GT; + case GtE: + return PyCmp_GE; + case Is: + return PyCmp_IS; + case IsNot: + return PyCmp_IS_NOT; + case In: + return PyCmp_IN; + case NotIn: + return PyCmp_NOT_IN; } - return 0; + return PyCmp_BAD; } static int -symtable_error(struct symtable *st, int lineno) -{ - if (lineno == 0) - lineno = st->st_cur->ste_lineno; - PyErr_SyntaxLocation(st->st_filename, lineno); - st->st_errors++; - return -1; +inplace_binop(struct compiler *c, operator_ty op) +{ + switch (op) { + case Add: + return INPLACE_ADD; + case Sub: + return INPLACE_SUBTRACT; + case Mult: + return INPLACE_MULTIPLY; + case Div: + if (c->c_flags && c->c_flags->cf_flags & CO_FUTURE_DIVISION) + return INPLACE_TRUE_DIVIDE; + else + return INPLACE_DIVIDE; + case Mod: + return INPLACE_MODULO; + case Pow: + return INPLACE_POWER; + case LShift: + return INPLACE_LSHIFT; + case RShift: + return INPLACE_RSHIFT; + case BitOr: + return INPLACE_OR; + case BitXor: + return INPLACE_XOR; + case BitAnd: + return INPLACE_AND; + case FloorDiv: + return INPLACE_FLOOR_DIVIDE; + } + assert(0); + return 0; } static int -symtable_load_symbols(struct compiling *c) +compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx) { - struct symtable *st = c->c_symtable; - PySymtableEntryObject *ste = st->st_cur; - PyObject *name, *varnames, *v; - int i, flags, pos; - struct symbol_info si; - - v = NULL; - - if (symtable_init_compiling_symbols(c) < 0) - goto fail; - symtable_init_info(&si); - varnames = st->st_cur->ste_varnames; - si.si_nlocals = PyList_GET_SIZE(varnames); - c->c_argcount = si.si_nlocals; - - for (i = 0; i < si.si_nlocals; ++i) { - v = PyInt_FromLong(i); - if (v == NULL) - goto fail; - if (PyDict_SetItem(c->c_locals, - PyList_GET_ITEM(varnames, i), v) < 0) - goto fail; - Py_DECREF(v); - } - - /* XXX The cases below define the rules for whether a name is - local or global. The logic could probably be clearer. */ - pos = 0; - while (PyDict_Next(ste->ste_symbols, &pos, &name, &v)) { - flags = PyInt_AS_LONG(v); + int op, scope; + enum { OP_FAST, OP_GLOBAL, OP_DEREF, OP_NAME } optype; - if (flags & DEF_FREE_GLOBAL) - /* undo the original DEF_FREE */ - flags &= ~(DEF_FREE | DEF_FREE_CLASS); + PyObject *dict = c->u->u_names; + /* XXX AugStore isn't used anywhere! */ - /* Deal with names that need two actions: - 1. Cell variables that are also locals. - 2. Free variables in methods that are also class - variables or declared global. - */ - if (flags & (DEF_FREE | DEF_FREE_CLASS)) - symtable_resolve_free(c, name, flags, &si); - - if (flags & DEF_STAR) { - c->c_argcount--; - c->c_flags |= CO_VARARGS; - } else if (flags & DEF_DOUBLESTAR) { - c->c_argcount--; - c->c_flags |= CO_VARKEYWORDS; - } else if (flags & DEF_INTUPLE) - c->c_argcount--; - else if (flags & DEF_GLOBAL) { - if (flags & DEF_PARAM) { - PyErr_Format(PyExc_SyntaxError, PARAM_GLOBAL, - PyString_AS_STRING(name)); - symtable_error(st, 0); - goto fail; - } - if (PyDict_SetItem(c->c_globals, name, Py_None) < 0) - goto fail; - } else if (flags & DEF_FREE_GLOBAL) { - si.si_nimplicit++; - if (PyDict_SetItem(c->c_globals, name, Py_True) < 0) - goto fail; - } else if ((flags & DEF_LOCAL) && !(flags & DEF_PARAM)) { - v = PyInt_FromLong(si.si_nlocals++); - if (v == NULL) - goto fail; - if (PyDict_SetItem(c->c_locals, name, v) < 0) - goto fail; - Py_DECREF(v); - if (ste->ste_type != TYPE_CLASS) - if (PyList_Append(c->c_varnames, name) < 0) - goto fail; - } else if (is_free(flags)) { - if (ste->ste_nested) { - v = PyInt_FromLong(si.si_nfrees++); - if (v == NULL) - goto fail; - if (PyDict_SetItem(c->c_freevars, name, v) < 0) - goto fail; - Py_DECREF(v); - } else { - si.si_nimplicit++; - if (PyDict_SetItem(c->c_globals, name, - Py_True) < 0) - goto fail; - if (st->st_nscopes != 1) { - v = PyInt_FromLong(flags); - if (v == NULL) - goto fail; - if (PyDict_SetItem(st->st_global, - name, v)) - goto fail; - Py_DECREF(v); - } - } - } + /* First check for assignment to __debug__. Param? */ + if ((ctx == Store || ctx == AugStore || ctx == Del) + && !strcmp(PyString_AS_STRING(name), "__debug__")) { + return compiler_error(c, "can not assign to __debug__"); } - assert(PyDict_Size(c->c_freevars) == si.si_nfrees); - if (si.si_ncells > 1) { /* one cell is always in order */ - if (symtable_cellvar_offsets(&c->c_cellvars, c->c_argcount, - c->c_varnames, c->c_flags) < 0) - return -1; + op = 0; + optype = OP_NAME; + scope = PyST_GetScope(c->u->u_ste, name); + switch (scope) { + case FREE: + dict = c->u->u_freevars; + optype = OP_DEREF; + break; + case CELL: + dict = c->u->u_cellvars; + optype = OP_DEREF; + break; + case LOCAL: + if (c->u->u_ste->ste_type == FunctionBlock) + optype = OP_FAST; + break; + case GLOBAL_IMPLICIT: + if (!c->u->u_ste->ste_unoptimized) + optype = OP_GLOBAL; + break; + case GLOBAL_EXPLICIT: + optype = OP_GLOBAL; + break; } - if (symtable_freevar_offsets(c->c_freevars, si.si_ncells) < 0) - return -1; - return symtable_update_flags(c, ste, &si); - fail: - /* is this always the right thing to do? */ - Py_XDECREF(v); - return -1; -} -static struct symtable * -symtable_init() -{ - struct symtable *st; + /* XXX Leave assert here, but handle __doc__ and the like better */ + assert(scope || PyString_AS_STRING(name)[0] == '_'); - st = (struct symtable *)PyObject_MALLOC(sizeof(struct symtable)); - if (st == NULL) - return NULL; - st->st_pass = 1; - - st->st_filename = NULL; - st->st_symbols = NULL; - if ((st->st_stack = PyList_New(0)) == NULL) - goto fail; - if ((st->st_symbols = PyDict_New()) == NULL) - goto fail; - st->st_cur = NULL; - st->st_nscopes = 0; - st->st_errors = 0; - st->st_private = NULL; - return st; - fail: - PySymtable_Free(st); - return NULL; -} + switch (optype) { + case OP_DEREF: + switch (ctx) { + case Load: op = LOAD_DEREF; break; + case Store: op = STORE_DEREF; break; + case AugLoad: + case AugStore: + break; + case Del: + PyErr_Format(PyExc_SyntaxError, + "can not delete variable '%s' referenced " + "in nested scope", + PyString_AS_STRING(name)); + return 0; + break; + case Param: + assert(0); /* impossible */ + } + break; + case OP_FAST: + switch (ctx) { + case Load: op = LOAD_FAST; break; + case Store: op = STORE_FAST; break; + case Del: op = DELETE_FAST; break; + case AugLoad: + case AugStore: + break; + case Param: + assert(0); /* impossible */ + } + ADDOP_O(c, op, name, varnames); + return 1; + case OP_GLOBAL: + switch (ctx) { + case Load: op = LOAD_GLOBAL; break; + case Store: op = STORE_GLOBAL; break; + case Del: op = DELETE_GLOBAL; break; + case AugLoad: + case AugStore: + break; + case Param: + assert(0); /* impossible */ + } + break; + case OP_NAME: + switch (ctx) { + case Load: op = LOAD_NAME; break; + case Store: op = STORE_NAME; break; + case Del: op = DELETE_NAME; break; + case AugLoad: + case AugStore: + break; + case Param: + assert(0); /* impossible */ + } + break; + } -void -PySymtable_Free(struct symtable *st) -{ - Py_XDECREF(st->st_symbols); - Py_XDECREF(st->st_stack); - Py_XDECREF(st->st_cur); - PyObject_FREE((void *)st); + assert(op); + return compiler_addop_name(c, op, dict, name); } -/* When the compiler exits a scope, it must should update the scope's - free variable information with the list of free variables in its - children. - - Variables that are free in children and defined in the current - scope are cellvars. - - If the scope being exited is defined at the top-level (ste_nested is - false), free variables in children that are not defined here are - implicit globals. - -*/ - static int -symtable_update_free_vars(struct symtable *st) +compiler_boolop(struct compiler *c, expr_ty e) { - int i, j, def; - PyObject *o, *name, *list = NULL; - PySymtableEntryObject *child, *ste = st->st_cur; + basicblock *end; + int jumpi, i, n; + asdl_seq *s; - if (ste->ste_type == TYPE_CLASS) - def = DEF_FREE_CLASS; + assert(e->kind == BoolOp_kind); + if (e->v.BoolOp.op == And) + jumpi = JUMP_IF_FALSE; else - def = DEF_FREE; - for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) { - int pos = 0; - - if (list && PyList_SetSlice(list, 0, - PyList_GET_SIZE(list), 0) < 0) - return -1; - child = (PySymtableEntryObject *) - PyList_GET_ITEM(ste->ste_children, i); - while (PyDict_Next(child->ste_symbols, &pos, &name, &o)) { - int flags = PyInt_AS_LONG(o); - if (!(is_free(flags))) - continue; /* avoids indentation */ - if (list == NULL) { - list = PyList_New(0); - if (list == NULL) - return -1; - } - ste->ste_child_free = 1; - if (PyList_Append(list, name) < 0) { - Py_DECREF(list); - return -1; - } - } - for (j = 0; list && j < PyList_GET_SIZE(list); j++) { - PyObject *v; - name = PyList_GET_ITEM(list, j); - v = PyDict_GetItem(ste->ste_symbols, name); - /* If a name N is declared global in scope A and - referenced in scope B contained (perhaps - indirectly) in A and there are no scopes - with bindings for N between B and A, then N - is global in B. Unless A is a class scope, - because class scopes are not considered for - nested scopes. - */ - if (v && (ste->ste_type != TYPE_CLASS)) { - int flags = PyInt_AS_LONG(v); - if (flags & DEF_GLOBAL) { - symtable_undo_free(st, child->ste_id, - name); - continue; - } - } - if (ste->ste_nested) { - if (symtable_add_def_o(st, ste->ste_symbols, - name, def) < 0) { - Py_DECREF(list); - return -1; - } - } else { - if (symtable_check_global(st, child->ste_id, - name) < 0) { - Py_DECREF(list); - return -1; - } - } - } - } - - Py_XDECREF(list); - return 0; + jumpi = JUMP_IF_TRUE; + end = compiler_new_block(c); + if (end < 0) + return 0; + s = e->v.BoolOp.values; + n = asdl_seq_LEN(s) - 1; + for (i = 0; i < n; ++i) { + VISIT(c, expr, asdl_seq_GET(s, i)); + ADDOP_JREL(c, jumpi, end); + ADDOP(c, POP_TOP) + } + VISIT(c, expr, asdl_seq_GET(s, n)); + compiler_use_next_block(c, end); + return 1; } -/* If the current scope is a non-nested class or if name is not - defined in the current, non-nested scope, then it is an implicit - global in all nested scopes. -*/ - static int -symtable_check_global(struct symtable *st, PyObject *child, PyObject *name) +compiler_list(struct compiler *c, expr_ty e) { - PyObject *o; - int v; - PySymtableEntryObject *ste = st->st_cur; - - if (ste->ste_type == TYPE_CLASS) - return symtable_undo_free(st, child, name); - o = PyDict_GetItem(ste->ste_symbols, name); - if (o == NULL) - return symtable_undo_free(st, child, name); - v = PyInt_AS_LONG(o); - - if (is_free(v) || (v & DEF_GLOBAL)) - return symtable_undo_free(st, child, name); - else - return symtable_add_def_o(st, ste->ste_symbols, - name, DEF_FREE); + int n = asdl_seq_LEN(e->v.List.elts); + if (e->v.List.ctx == Store) { + ADDOP_I(c, UNPACK_SEQUENCE, n); + } + VISIT_SEQ(c, expr, e->v.List.elts); + if (e->v.List.ctx == Load) { + ADDOP_I(c, BUILD_LIST, n); + } + return 1; } static int -symtable_undo_free(struct symtable *st, PyObject *id, - PyObject *name) +compiler_tuple(struct compiler *c, expr_ty e) { - int i, v, x; - PyObject *info; - PySymtableEntryObject *ste; - - ste = (PySymtableEntryObject *)PyDict_GetItem(st->st_symbols, id); - if (ste == NULL) - return -1; - - info = PyDict_GetItem(ste->ste_symbols, name); - if (info == NULL) - return 0; - v = PyInt_AS_LONG(info); - if (is_free(v)) { - if (symtable_add_def_o(st, ste->ste_symbols, name, - DEF_FREE_GLOBAL) < 0) - return -1; - } else - /* If the name is defined here or declared global, - then the recursion stops. */ - return 0; - - for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) { - PySymtableEntryObject *child; - child = (PySymtableEntryObject *) - PyList_GET_ITEM(ste->ste_children, i); - x = symtable_undo_free(st, child->ste_id, name); - if (x < 0) - return x; + int n = asdl_seq_LEN(e->v.Tuple.elts); + if (e->v.Tuple.ctx == Store) { + ADDOP_I(c, UNPACK_SEQUENCE, n); } - return 0; + VISIT_SEQ(c, expr, e->v.Tuple.elts); + if (e->v.Tuple.ctx == Load) { + ADDOP_I(c, BUILD_TUPLE, n); + } + return 1; } -/* symtable_enter_scope() gets a reference via PySymtableEntry_New(). - This reference is released when the scope is exited, via the DECREF - in symtable_exit_scope(). -*/ - static int -symtable_exit_scope(struct symtable *st) -{ - int end; - - if (st->st_pass == 1) - symtable_update_free_vars(st); - Py_DECREF(st->st_cur); - end = PyList_GET_SIZE(st->st_stack) - 1; - st->st_cur = (PySymtableEntryObject *)PyList_GET_ITEM(st->st_stack, - end); - if (PySequence_DelItem(st->st_stack, end) < 0) - return -1; - return 0; +compiler_compare(struct compiler *c, expr_ty e) +{ + int i, n; + basicblock *cleanup = NULL; + + /* XXX the logic can be cleaned up for 1 or multiple comparisons */ + VISIT(c, expr, e->v.Compare.left); + n = asdl_seq_LEN(e->v.Compare.ops); + assert(n > 0); + if (n > 1) { + cleanup = compiler_new_block(c); + if (cleanup == NULL) + return 0; + VISIT(c, expr, asdl_seq_GET(e->v.Compare.comparators, 0)); + } + for (i = 1; i < n; i++) { + ADDOP(c, DUP_TOP); + ADDOP(c, ROT_THREE); + /* XXX We're casting a void* to cmpop_ty in the next stmt. */ + ADDOP_I(c, COMPARE_OP, + cmpop((cmpop_ty)asdl_seq_GET(e->v.Compare.ops, i - 1))); + ADDOP_JREL(c, JUMP_IF_FALSE, cleanup); + NEXT_BLOCK(c); + ADDOP(c, POP_TOP); + if (i < (n - 1)) + VISIT(c, expr, asdl_seq_GET(e->v.Compare.comparators, i)); + } + VISIT(c, expr, asdl_seq_GET(e->v.Compare.comparators, n - 1)); + ADDOP_I(c, COMPARE_OP, + /* XXX We're casting a void* to cmpop_ty in the next stmt. */ + cmpop((cmpop_ty)asdl_seq_GET(e->v.Compare.ops, n - 1))); + if (n > 1) { + basicblock *end = compiler_new_block(c); + if (end == NULL) + return 0; + ADDOP_JREL(c, JUMP_FORWARD, end); + compiler_use_next_block(c, cleanup); + ADDOP(c, ROT_TWO); + ADDOP(c, POP_TOP); + compiler_use_next_block(c, end); + } + return 1; } -static void -symtable_enter_scope(struct symtable *st, char *name, int type, - int lineno) +static int +compiler_call(struct compiler *c, expr_ty e) { - PySymtableEntryObject *prev = NULL; + int n, code = 0; - if (st->st_cur) { - prev = st->st_cur; - if (PyList_Append(st->st_stack, (PyObject *)st->st_cur) < 0) { - st->st_errors++; - return; - } + VISIT(c, expr, e->v.Call.func); + n = asdl_seq_LEN(e->v.Call.args); + VISIT_SEQ(c, expr, e->v.Call.args); + if (e->v.Call.keywords) { + VISIT_SEQ(c, keyword, e->v.Call.keywords); + n |= asdl_seq_LEN(e->v.Call.keywords) << 8; } - st->st_cur = (PySymtableEntryObject *) - PySymtableEntry_New(st, name, type, lineno); - if (st->st_cur == NULL) { - st->st_errors++; - return; + if (e->v.Call.starargs) { + VISIT(c, expr, e->v.Call.starargs); + code |= 1; } - if (strcmp(name, TOP) == 0) - st->st_global = st->st_cur->ste_symbols; - if (prev && st->st_pass == 1) { - if (PyList_Append(prev->ste_children, - (PyObject *)st->st_cur) < 0) - st->st_errors++; + if (e->v.Call.kwargs) { + VISIT(c, expr, e->v.Call.kwargs); + code |= 2; + } + switch (code) { + case 0: + ADDOP_I(c, CALL_FUNCTION, n); + break; + case 1: + ADDOP_I(c, CALL_FUNCTION_VAR, n); + break; + case 2: + ADDOP_I(c, CALL_FUNCTION_KW, n); + break; + case 3: + ADDOP_I(c, CALL_FUNCTION_VAR_KW, n); + break; } + return 1; } static int -symtable_lookup(struct symtable *st, char *name) -{ - char buffer[MANGLE_LEN]; - PyObject *v; - int flags; - - if (_Py_Mangle(st->st_private, name, buffer, sizeof(buffer))) - name = buffer; - v = PyDict_GetItemString(st->st_cur->ste_symbols, name); - if (v == NULL) { - if (PyErr_Occurred()) - return -1; - else - return 0; - } - - flags = PyInt_AS_LONG(v); - return flags; +compiler_listcomp_generator(struct compiler *c, PyObject *tmpname, + asdl_seq *generators, int gen_index, + expr_ty elt) +{ + /* generate code for the iterator, then each of the ifs, + and then write to the element */ + + comprehension_ty l; + basicblock *start, *anchor, *skip, *if_cleanup; + int i, n; + + start = compiler_new_block(c); + skip = compiler_new_block(c); + if_cleanup = compiler_new_block(c); + anchor = compiler_new_block(c); + + if (start == NULL || skip == NULL || if_cleanup == NULL || + anchor == NULL) + return 0; + + l = asdl_seq_GET(generators, gen_index); + VISIT(c, expr, l->iter); + ADDOP(c, GET_ITER); + compiler_use_next_block(c, start); + ADDOP_JREL(c, FOR_ITER, anchor); + NEXT_BLOCK(c); + VISIT(c, expr, l->target); + + /* XXX this needs to be cleaned up...a lot! */ + n = asdl_seq_LEN(l->ifs); + for (i = 0; i < n; i++) { + expr_ty e = asdl_seq_GET(l->ifs, i); + VISIT(c, expr, e); + ADDOP_JREL(c, JUMP_IF_FALSE, if_cleanup); + NEXT_BLOCK(c); + ADDOP(c, POP_TOP); + } + + if (++gen_index < asdl_seq_LEN(generators)) + if (!compiler_listcomp_generator(c, tmpname, + generators, gen_index, elt)) + return 0; + + /* only append after the last for generator */ + if (gen_index >= asdl_seq_LEN(generators)) { + if (!compiler_nameop(c, tmpname, Load)) + return 0; + VISIT(c, expr, elt); + ADDOP_I(c, CALL_FUNCTION, 1); + ADDOP(c, POP_TOP); + + compiler_use_next_block(c, skip); + } + for (i = 0; i < n; i++) { + ADDOP_I(c, JUMP_FORWARD, 1); + if (i == 0) + compiler_use_next_block(c, if_cleanup); + ADDOP(c, POP_TOP); + } + ADDOP_JABS(c, JUMP_ABSOLUTE, start); + compiler_use_next_block(c, anchor); + /* delete the append method added to locals */ + if (gen_index == 1) + if (!compiler_nameop(c, tmpname, Del)) + return 0; + + return 1; } static int -symtable_add_def(struct symtable *st, char *name, int flag) -{ - PyObject *s; - char buffer[MANGLE_LEN]; - int ret; - - /* Warn about None, except inside a tuple (where the assignment - code already issues a warning). */ - if ((flag & DEF_PARAM) && !(flag & DEF_INTUPLE) && - *name == 'N' && strcmp(name, "None") == 0) - { - PyErr_SetString(PyExc_SyntaxError, - "Invalid syntax. Assignment to None."); - symtable_error(st, 0); - return -1; +compiler_listcomp(struct compiler *c, expr_ty e) +{ + char tmpname[256]; + identifier tmp; + int rc = 0; + static identifier append; + asdl_seq *generators = e->v.ListComp.generators; + + assert(e->kind == ListComp_kind); + if (!append) { + append = PyString_InternFromString("append"); + if (!append) + return 0; } - if (_Py_Mangle(st->st_private, name, buffer, sizeof(buffer))) - name = buffer; - if ((s = PyString_InternFromString(name)) == NULL) - return -1; - ret = symtable_add_def_o(st, st->st_cur->ste_symbols, s, flag); - Py_DECREF(s); - return ret; + PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", ++c->u->u_tmpname); + tmp = PyString_FromString(tmpname); + if (!tmp) + return 0; + ADDOP_I(c, BUILD_LIST, 0); + ADDOP(c, DUP_TOP); + ADDOP_O(c, LOAD_ATTR, append, names); + if (compiler_nameop(c, tmp, Store)) + rc = compiler_listcomp_generator(c, tmp, generators, 0, + e->v.ListComp.elt); + Py_DECREF(tmp); + return rc; } -/* Must only be called with mangled names */ - static int -symtable_add_def_o(struct symtable *st, PyObject *dict, - PyObject *name, int flag) +compiler_genexp_generator(struct compiler *c, + asdl_seq *generators, int gen_index, + expr_ty elt) { - PyObject *o; - int val; - - if ((o = PyDict_GetItem(dict, name))) { - val = PyInt_AS_LONG(o); - if ((flag & DEF_PARAM) && (val & DEF_PARAM)) { - PyErr_Format(PyExc_SyntaxError, DUPLICATE_ARGUMENT, - PyString_AsString(name)); - return symtable_error(st, 0); - } - val |= flag; - } else - val = flag; - o = PyInt_FromLong(val); - if (o == NULL) - return -1; - if (PyDict_SetItem(dict, name, o) < 0) { - Py_DECREF(o); - return -1; - } - Py_DECREF(o); + /* generate code for the iterator, then each of the ifs, + and then write to the element */ - if (flag & DEF_PARAM) { - if (PyList_Append(st->st_cur->ste_varnames, name) < 0) - return -1; - } else if (flag & DEF_GLOBAL) { - /* XXX need to update DEF_GLOBAL for other flags too; - perhaps only DEF_FREE_GLOBAL */ - if ((o = PyDict_GetItem(st->st_global, name))) { - val = PyInt_AS_LONG(o); - val |= flag; - } else - val = flag; - o = PyInt_FromLong(val); - if (o == NULL) - return -1; - if (PyDict_SetItem(st->st_global, name, o) < 0) { - Py_DECREF(o); - return -1; - } - Py_DECREF(o); + comprehension_ty ge; + basicblock *start, *anchor, *skip, *if_cleanup, *end; + int i, n; + + start = compiler_new_block(c); + skip = compiler_new_block(c); + if_cleanup = compiler_new_block(c); + anchor = compiler_new_block(c); + end = compiler_new_block(c); + + if (start == NULL || skip == NULL || if_cleanup == NULL || + anchor == NULL || end == NULL) + return 0; + + ge = asdl_seq_GET(generators, gen_index); + ADDOP_JREL(c, SETUP_LOOP, end); + if (!compiler_push_fblock(c, LOOP, start)) + return 0; + + if (gen_index == 0) { + /* Receive outermost iter as an implicit argument */ + c->u->u_argcount = 1; + ADDOP_I(c, LOAD_FAST, 0); } - return 0; -} + else { + /* Sub-iter - calculate on the fly */ + VISIT(c, expr, ge->iter); + ADDOP(c, GET_ITER); + } + compiler_use_next_block(c, start); + ADDOP_JREL(c, FOR_ITER, anchor); + NEXT_BLOCK(c); + VISIT(c, expr, ge->target); + + /* XXX this needs to be cleaned up...a lot! */ + n = asdl_seq_LEN(ge->ifs); + for (i = 0; i < n; i++) { + expr_ty e = asdl_seq_GET(ge->ifs, i); + VISIT(c, expr, e); + ADDOP_JREL(c, JUMP_IF_FALSE, if_cleanup); + NEXT_BLOCK(c); + ADDOP(c, POP_TOP); + } + + if (++gen_index < asdl_seq_LEN(generators)) + if (!compiler_genexp_generator(c, generators, gen_index, elt)) + return 0; -#define symtable_add_use(ST, NAME) symtable_add_def((ST), (NAME), USE) + /* only append after the last 'for' generator */ + if (gen_index >= asdl_seq_LEN(generators)) { + VISIT(c, expr, elt); + ADDOP(c, YIELD_VALUE); + ADDOP(c, POP_TOP); + + compiler_use_next_block(c, skip); + } + for (i = 0; i < n; i++) { + ADDOP_I(c, JUMP_FORWARD, 1); + if (i == 0) + compiler_use_next_block(c, if_cleanup); + + ADDOP(c, POP_TOP); + } + ADDOP_JABS(c, JUMP_ABSOLUTE, start); + compiler_use_next_block(c, anchor); + ADDOP(c, POP_BLOCK); + compiler_pop_fblock(c, LOOP, start); + compiler_use_next_block(c, end); + + return 1; +} -/* Look for a yield stmt or expr under n. Return 1 if found, else 0. - This hack is used to look inside "if 0:" blocks (which are normally - ignored) in case those are the only places a yield occurs (so that this - function is a generator). */ static int -look_for_yield(node *n) +compiler_genexp(struct compiler *c, expr_ty e) { - int i; - - for (i = 0; i < NCH(n); ++i) { - node *kid = CHILD(n, i); + PyObject *name; + PyCodeObject *co; + expr_ty outermost_iter = ((comprehension_ty) + (asdl_seq_GET(e->v.GeneratorExp.generators, + 0)))->iter; - switch (TYPE(kid)) { + name = PyString_FromString(""); + if (!name) + return 0; - case classdef: - case funcdef: - case lambdef: - /* Stuff in nested functions and classes can't make - the parent a generator. */ - return 0; + if (!compiler_enter_scope(c, name, (void *)e, e->lineno)) + return 0; + compiler_genexp_generator(c, e->v.GeneratorExp.generators, 0, + e->v.GeneratorExp.elt); + co = assemble(c, 1); + if (co == NULL) + return 0; + compiler_exit_scope(c); - case yield_stmt: - case yield_expr: - return GENERATOR; + compiler_make_closure(c, co, 0); + VISIT(c, expr, outermost_iter); + ADDOP(c, GET_ITER); + ADDOP_I(c, CALL_FUNCTION, 1); + Py_DECREF(name); + Py_DECREF(co); - default: - if (look_for_yield(kid)) - return GENERATOR; - } - } - return 0; -} + return 1; +} -static void -symtable_node(struct symtable *st, node *n) +static int +compiler_visit_keyword(struct compiler *c, keyword_ty k) { - int i; + ADDOP_O(c, LOAD_CONST, k->arg, consts); + VISIT(c, expr, k->value); + return 1; +} - loop: - switch (TYPE(n)) { - case funcdef: { - char *func_name; - if (NCH(n) == 6) - symtable_node(st, CHILD(n, 0)); - func_name = STR(RCHILD(n, -4)); - symtable_add_def(st, func_name, DEF_LOCAL); - symtable_default_args(st, RCHILD(n, -3)); - symtable_enter_scope(st, func_name, TYPE(n), n->n_lineno); - symtable_funcdef(st, n); - symtable_exit_scope(st); - break; - } - case lambdef: - if (NCH(n) == 4) - symtable_default_args(st, CHILD(n, 1)); - symtable_enter_scope(st, "lambda", TYPE(n), n->n_lineno); - symtable_funcdef(st, n); - symtable_exit_scope(st); - break; - case classdef: { - char *tmp, *class_name = STR(CHILD(n, 1)); - symtable_add_def(st, class_name, DEF_LOCAL); - if (TYPE(CHILD(n, 2)) == LPAR) { - node *bases = CHILD(n, 3); - int i; - for (i = 0; i < NCH(bases); i += 2) { - symtable_node(st, CHILD(bases, i)); - } - } - symtable_enter_scope(st, class_name, TYPE(n), n->n_lineno); - tmp = st->st_private; - st->st_private = class_name; - symtable_node(st, CHILD(n, NCH(n) - 1)); - st->st_private = tmp; - symtable_exit_scope(st); - break; - } - case if_stmt: - for (i = 0; i + 3 < NCH(n); i += 4) { - if (is_constant_false(NULL, (CHILD(n, i + 1)))) { - if (st->st_cur->ste_generator == 0) - st->st_cur->ste_generator = - look_for_yield(CHILD(n, i+3)); - continue; - } - symtable_node(st, CHILD(n, i + 1)); - symtable_node(st, CHILD(n, i + 3)); - } - if (i + 2 < NCH(n)) - symtable_node(st, CHILD(n, i + 2)); - break; - case global_stmt: - symtable_global(st, n); - break; - case import_stmt: - symtable_import(st, n); - break; - case exec_stmt: { - st->st_cur->ste_optimized |= OPT_EXEC; - symtable_node(st, CHILD(n, 1)); - if (NCH(n) > 2) - symtable_node(st, CHILD(n, 3)); - else { - st->st_cur->ste_optimized |= OPT_BARE_EXEC; - st->st_cur->ste_opt_lineno = n->n_lineno; - } - if (NCH(n) > 4) - symtable_node(st, CHILD(n, 5)); - break; +/* Test whether expression is constant. For constants, report + whether they are true or false. + + Return values: 1 for true, 0 for false, -1 for non-constant. + */ +static int +expr_constant(expr_ty e) +{ + switch (e->kind) { + case Num_kind: + return PyObject_IsTrue(e->v.Num.n); + case Str_kind: + return PyObject_IsTrue(e->v.Str.s); + default: + return -1; } - case assert_stmt: - if (Py_OptimizeFlag) - return; - if (NCH(n) == 2) { - n = CHILD(n, 1); - goto loop; - } else { - symtable_node(st, CHILD(n, 1)); - n = CHILD(n, 3); - goto loop; - } - case except_clause: - if (NCH(n) == 4) - symtable_assign(st, CHILD(n, 3), 0); - if (NCH(n) > 1) { - n = CHILD(n, 1); - goto loop; +} + +static int +compiler_visit_expr(struct compiler *c, expr_ty e) +{ + int i, n; + + if (e->lineno > c->u->u_lineno) { + c->u->u_lineno = e->lineno; + c->u->u_lineno_set = false; + } + switch (e->kind) { + case BoolOp_kind: + return compiler_boolop(c, e); + case BinOp_kind: + VISIT(c, expr, e->v.BinOp.left); + VISIT(c, expr, e->v.BinOp.right); + ADDOP(c, binop(c, e->v.BinOp.op)); + break; + case UnaryOp_kind: + VISIT(c, expr, e->v.UnaryOp.operand); + ADDOP(c, unaryop(e->v.UnaryOp.op)); + break; + case Lambda_kind: + return compiler_lambda(c, e); + case Dict_kind: + /* XXX get rid of arg? */ + ADDOP_I(c, BUILD_MAP, 0); + n = asdl_seq_LEN(e->v.Dict.values); + /* We must arrange things just right for STORE_SUBSCR. + It wants the stack to look like (value) (dict) (key) */ + for (i = 0; i < n; i++) { + ADDOP(c, DUP_TOP); + VISIT(c, expr, asdl_seq_GET(e->v.Dict.values, i)); + ADDOP(c, ROT_TWO); + VISIT(c, expr, asdl_seq_GET(e->v.Dict.keys, i)); + ADDOP(c, STORE_SUBSCR); + } + break; + case ListComp_kind: + return compiler_listcomp(c, e); + case GeneratorExp_kind: + return compiler_genexp(c, e); + case Yield_kind: + if (c->u->u_ste->ste_type != FunctionBlock) + return compiler_error(c, "'yield' outside function"); + /* + for (i = 0; i < c->u->u_nfblocks; i++) { + if (c->u->u_fblock[i].fb_type == FINALLY_TRY) + return compiler_error( + c, "'yield' not allowed in a 'try' " + "block with a 'finally' clause"); } - break; - case del_stmt: - symtable_assign(st, CHILD(n, 1), 0); - break; - case yield_expr: - st->st_cur->ste_generator = 1; - if (NCH(n)==1) - break; - n = CHILD(n, 1); - goto loop; - case expr_stmt: - if (NCH(n) == 1) - n = CHILD(n, 0); - else { - if (TYPE(CHILD(n, 1)) == augassign) { - symtable_assign(st, CHILD(n, 0), 0); - symtable_node(st, CHILD(n, 2)); - break; - } else { - int i; - for (i = 0; i < NCH(n) - 2; i += 2) - symtable_assign(st, CHILD(n, i), 0); - n = CHILD(n, NCH(n) - 1); - } + */ + if (e->v.Yield.value) { + VISIT(c, expr, e->v.Yield.value); } - goto loop; - case list_iter: - /* only occurs when there are multiple for loops - in a list comprehension */ - n = CHILD(n, 0); - if (TYPE(n) == list_for) - symtable_list_for(st, n); else { - REQ(n, list_if); - symtable_node(st, CHILD(n, 1)); - if (NCH(n) == 3) { - n = CHILD(n, 2); - goto loop; - } + ADDOP_O(c, LOAD_CONST, Py_None, consts); + } + ADDOP(c, YIELD_VALUE); + break; + case Compare_kind: + return compiler_compare(c, e); + case Call_kind: + return compiler_call(c, e); + case Repr_kind: + VISIT(c, expr, e->v.Repr.value); + ADDOP(c, UNARY_CONVERT); + break; + case Num_kind: + ADDOP_O(c, LOAD_CONST, e->v.Num.n, consts); + break; + case Str_kind: + ADDOP_O(c, LOAD_CONST, e->v.Str.s, consts); + break; + /* The following exprs can be assignment targets. */ + case Attribute_kind: + if (e->v.Attribute.ctx != AugStore) + VISIT(c, expr, e->v.Attribute.value); + switch (e->v.Attribute.ctx) { + case AugLoad: + ADDOP(c, DUP_TOP); + /* Fall through to load */ + case Load: + ADDOP_NAME(c, LOAD_ATTR, e->v.Attribute.attr, names); + break; + case AugStore: + ADDOP(c, ROT_TWO); + /* Fall through to save */ + case Store: + ADDOP_NAME(c, STORE_ATTR, e->v.Attribute.attr, names); + break; + case Del: + ADDOP_NAME(c, DELETE_ATTR, e->v.Attribute.attr, names); + break; + case Param: + assert(0); + break; } break; - case for_stmt: - symtable_assign(st, CHILD(n, 1), 0); - for (i = 3; i < NCH(n); ++i) - if (TYPE(CHILD(n, i)) >= single_input) - symtable_node(st, CHILD(n, i)); - break; - case arglist: - if (NCH(n) > 1) - for (i = 0; i < NCH(n); ++i) { - node *ch = CHILD(n, i); - if (TYPE(ch) == argument && NCH(ch) == 2 && - TYPE(CHILD(ch, 1)) == gen_for) { - PyErr_SetString(PyExc_SyntaxError, - "invalid syntax"); - symtable_error(st, n->n_lineno); - return; - } - } - /* The remaining cases fall through to default except in - special circumstances. This requires the individual cases - to be coded with great care, even though they look like - rather innocuous. Each case must double-check TYPE(n). - */ - case decorator: - if (TYPE(n) == decorator) { - /* decorator: '@' dotted_name [ '(' [arglist] ')' ] */ - node *name, *varname; - name = CHILD(n, 1); - REQ(name, dotted_name); - varname = CHILD(name, 0); - REQ(varname, NAME); - symtable_add_use(st, STR(varname)); - } - /* fall through */ - case argument: - if (TYPE(n) == argument && NCH(n) == 3) { - n = CHILD(n, 2); - goto loop; - } - else if (TYPE(n) == argument && NCH(n) == 2 && - TYPE(CHILD(n, 1)) == gen_for) { - symtable_generator_expression(st, n); + case Subscript_kind: + switch (e->v.Subscript.ctx) { + case AugLoad: + VISIT(c, expr, e->v.Subscript.value); + VISIT_SLICE(c, e->v.Subscript.slice, AugLoad); break; - } - /* fall through */ - case listmaker: - if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) { - symtable_list_comprehension(st, n); + case Load: + VISIT(c, expr, e->v.Subscript.value); + VISIT_SLICE(c, e->v.Subscript.slice, Load); break; - } - /* fall through */ - case testlist_gexp: - if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == gen_for) { - symtable_generator_expression(st, n); + case AugStore: + VISIT_SLICE(c, e->v.Subscript.slice, AugStore); + break; + case Store: + VISIT(c, expr, e->v.Subscript.value); + VISIT_SLICE(c, e->v.Subscript.slice, Store); + break; + case Del: + VISIT(c, expr, e->v.Subscript.value); + VISIT_SLICE(c, e->v.Subscript.slice, Del); + break; + case Param: + assert(0); break; } - /* fall through */ - - case atom: - if (TYPE(n) == atom) { - if (TYPE(CHILD(n, 0)) == NAME) { - symtable_add_use(st, STR(CHILD(n, 0))); - break; - } - else if (TYPE(CHILD(n,0)) == LPAR) { - n = CHILD(n,1); - goto loop; - } - } - /* fall through */ + break; + case Name_kind: + return compiler_nameop(c, e->v.Name.id, e->v.Name.ctx); + /* child nodes of List and Tuple will have expr_context set */ + case List_kind: + return compiler_list(c, e); + case Tuple_kind: + return compiler_tuple(c, e); + } + return 1; +} + +static int +compiler_augassign(struct compiler *c, stmt_ty s) +{ + expr_ty e = s->v.AugAssign.target; + expr_ty auge; + + assert(s->kind == AugAssign_kind); + + switch (e->kind) { + case Attribute_kind: + auge = Attribute(e->v.Attribute.value, e->v.Attribute.attr, + AugLoad, e->lineno); + if (auge == NULL) + return 0; + VISIT(c, expr, auge); + VISIT(c, expr, s->v.AugAssign.value); + ADDOP(c, inplace_binop(c, s->v.AugAssign.op)); + auge->v.Attribute.ctx = AugStore; + VISIT(c, expr, auge); + free(auge); + break; + case Subscript_kind: + auge = Subscript(e->v.Subscript.value, e->v.Subscript.slice, + AugLoad, e->lineno); + if (auge == NULL) + return 0; + VISIT(c, expr, auge); + VISIT(c, expr, s->v.AugAssign.value); + ADDOP(c, inplace_binop(c, s->v.AugAssign.op)); + auge->v.Subscript.ctx = AugStore; + VISIT(c, expr, auge); + free(auge); + break; + case Name_kind: + VISIT(c, expr, s->v.AugAssign.target); + VISIT(c, expr, s->v.AugAssign.value); + ADDOP(c, inplace_binop(c, s->v.AugAssign.op)); + return compiler_nameop(c, e->v.Name.id, Store); default: - /* Walk over every non-token child with a special case - for one child. - */ - if (NCH(n) == 1) { - n = CHILD(n, 0); - goto loop; - } - for (i = 0; i < NCH(n); ++i) - if (TYPE(CHILD(n, i)) >= single_input) - symtable_node(st, CHILD(n, i)); + fprintf(stderr, + "invalid node type for augmented assignment\n"); + return 0; } + return 1; } -static void -symtable_funcdef(struct symtable *st, node *n) +static int +compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b) { - node *body; - - if (TYPE(n) == lambdef) { - if (NCH(n) == 4) - symtable_params(st, CHILD(n, 1)); - } else - symtable_params(st, RCHILD(n, -3)); - body = CHILD(n, NCH(n) - 1); - symtable_node(st, body); + struct fblockinfo *f; + if (c->u->u_nfblocks >= CO_MAXBLOCKS) + return 0; + f = &c->u->u_fblock[c->u->u_nfblocks++]; + f->fb_type = t; + f->fb_block = b; + return 1; } -/* The next two functions parse the argument tuple. - symtable_default_args() checks for names in the default arguments, - which are references in the defining scope. symtable_params() - parses the parameter names, which are defined in the function's - body. +static void +compiler_pop_fblock(struct compiler *c, enum fblocktype t, basicblock *b) +{ + struct compiler_unit *u = c->u; + assert(u->u_nfblocks > 0); + u->u_nfblocks--; + assert(u->u_fblock[u->u_nfblocks].fb_type == t); + assert(u->u_fblock[u->u_nfblocks].fb_block == b); +} - varargslist: - (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) - | fpdef ['=' test] (',' fpdef ['=' test])* [','] +/* Raises a SyntaxError and returns 0. + If something goes wrong, a different exception may be raised. */ -static void -symtable_default_args(struct symtable *st, node *n) +static int +compiler_error(struct compiler *c, const char *errstr) { - node *c; - int i; + PyObject *loc; + PyObject *u = NULL, *v = NULL; - if (TYPE(n) == parameters) { - n = CHILD(n, 1); - if (TYPE(n) == RPAR) - return; - } - REQ(n, varargslist); - for (i = 0; i < NCH(n); i += 2) { - c = CHILD(n, i); - if (TYPE(c) == STAR || TYPE(c) == DOUBLESTAR) { - break; - } - if (i > 0 && (TYPE(CHILD(n, i - 1)) == EQUAL)) - symtable_node(st, CHILD(n, i)); + loc = PyErr_ProgramText(c->c_filename, c->u->u_lineno); + if (!loc) { + Py_INCREF(Py_None); + loc = Py_None; } + u = Py_BuildValue("(ziOO)", c->c_filename, c->u->u_lineno, + Py_None, loc); + if (!u) + goto exit; + v = Py_BuildValue("(zO)", errstr, u); + if (!v) + goto exit; + PyErr_SetObject(PyExc_SyntaxError, v); + exit: + Py_DECREF(loc); + Py_XDECREF(u); + Py_XDECREF(v); + return 0; } -static void -symtable_params(struct symtable *st, node *n) +static int +compiler_handle_subscr(struct compiler *c, const char *kind, + expr_context_ty ctx) +{ + int op = 0; + + /* XXX this code is duplicated */ + switch (ctx) { + case AugLoad: /* fall through to Load */ + case Load: op = BINARY_SUBSCR; break; + case AugStore:/* fall through to Store */ + case Store: op = STORE_SUBSCR; break; + case Del: op = DELETE_SUBSCR; break; + case Param: + fprintf(stderr, + "invalid %s kind %d in subscript\n", + kind, ctx); + return 0; + } + if (ctx == AugLoad) { + ADDOP_I(c, DUP_TOPX, 2); + } + else if (ctx == AugStore) { + ADDOP(c, ROT_THREE); + } + ADDOP(c, op); + return 1; +} + +static int +compiler_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) { - int i, complex = -1, ext = 0; - node *c = NULL; + int n = 2; + assert(s->kind == Slice_kind); - if (TYPE(n) == parameters) { - n = CHILD(n, 1); - if (TYPE(n) == RPAR) - return; + /* only handles the cases where BUILD_SLICE is emitted */ + if (s->v.Slice.lower) { + VISIT(c, expr, s->v.Slice.lower); } - REQ(n, varargslist); - for (i = 0; i < NCH(n); i += 2) { - c = CHILD(n, i); - if (TYPE(c) == STAR || TYPE(c) == DOUBLESTAR) { - ext = 1; - break; - } - if (TYPE(c) == test) { - continue; - } - if (TYPE(CHILD(c, 0)) == NAME) - symtable_add_def(st, STR(CHILD(c, 0)), DEF_PARAM); - else { - char nbuf[30]; - PyOS_snprintf(nbuf, sizeof(nbuf), ".%d", i); - symtable_add_def(st, nbuf, DEF_PARAM); - complex = i; - } + else { + ADDOP_O(c, LOAD_CONST, Py_None, consts); } - if (ext) { - c = CHILD(n, i); - if (TYPE(c) == STAR) { - i++; - symtable_add_def(st, STR(CHILD(n, i)), - DEF_PARAM | DEF_STAR); - i += 2; - if (i >= NCH(n)) - c = NULL; - else - c = CHILD(n, i); + + if (s->v.Slice.upper) { + VISIT(c, expr, s->v.Slice.upper); + } + else { + ADDOP_O(c, LOAD_CONST, Py_None, consts); + } + + if (s->v.Slice.step) { + n++; + VISIT(c, expr, s->v.Slice.step); + } + ADDOP_I(c, BUILD_SLICE, n); + return 1; +} + +static int +compiler_simple_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) +{ + int op = 0, slice_offset = 0, stack_count = 0; + + assert(s->v.Slice.step == NULL); + if (s->v.Slice.lower) { + slice_offset++; + stack_count++; + if (ctx != AugStore) + VISIT(c, expr, s->v.Slice.lower); + } + if (s->v.Slice.upper) { + slice_offset += 2; + stack_count++; + if (ctx != AugStore) + VISIT(c, expr, s->v.Slice.upper); + } + + if (ctx == AugLoad) { + switch (stack_count) { + case 0: ADDOP(c, DUP_TOP); break; + case 1: ADDOP_I(c, DUP_TOPX, 2); break; + case 2: ADDOP_I(c, DUP_TOPX, 3); break; + } + } + else if (ctx == AugStore) { + switch (stack_count) { + case 0: ADDOP(c, ROT_TWO); break; + case 1: ADDOP(c, ROT_THREE); break; + case 2: ADDOP(c, ROT_FOUR); break; + } + } + + switch (ctx) { + case AugLoad: /* fall through to Load */ + case Load: op = SLICE; break; + case AugStore:/* fall through to Store */ + case Store: op = STORE_SLICE; break; + case Del: op = DELETE_SLICE; break; + case Param: /* XXX impossible? */ + fprintf(stderr, "param invalid\n"); + assert(0); + } + + ADDOP(c, op + slice_offset); + return 1; +} + +static int +compiler_visit_nested_slice(struct compiler *c, slice_ty s, + expr_context_ty ctx) +{ + switch (s->kind) { + case Ellipsis_kind: + ADDOP_O(c, LOAD_CONST, Py_Ellipsis, consts); + break; + case Slice_kind: + return compiler_slice(c, s, ctx); + break; + case Index_kind: + VISIT(c, expr, s->v.Index.value); + break; + case ExtSlice_kind: + assert(0); + break; + } + return 1; +} + + +static int +compiler_visit_slice(struct compiler *c, slice_ty s, expr_context_ty ctx) +{ + switch (s->kind) { + case Ellipsis_kind: + ADDOP_O(c, LOAD_CONST, Py_Ellipsis, consts); + break; + case Slice_kind: + if (!s->v.Slice.step) + return compiler_simple_slice(c, s, ctx); + if (!compiler_slice(c, s, ctx)) + return 0; + if (ctx == AugLoad) { + ADDOP_I(c, DUP_TOPX, 2); } - if (c && TYPE(c) == DOUBLESTAR) { - i++; - symtable_add_def(st, STR(CHILD(n, i)), - DEF_PARAM | DEF_DOUBLESTAR); + else if (ctx == AugStore) { + ADDOP(c, ROT_THREE); + } + return compiler_handle_subscr(c, "slice", ctx); + break; + case ExtSlice_kind: { + int i, n = asdl_seq_LEN(s->v.ExtSlice.dims); + for (i = 0; i < n; i++) { + slice_ty sub = asdl_seq_GET(s->v.ExtSlice.dims, i); + if (!compiler_visit_nested_slice(c, sub, ctx)) + return 0; } + ADDOP_I(c, BUILD_TUPLE, n); + return compiler_handle_subscr(c, "extended slice", ctx); + break; } - if (complex >= 0) { - int j; - for (j = 0; j <= complex; j++) { - c = CHILD(n, j); - if (TYPE(c) == COMMA) - c = CHILD(n, ++j); - else if (TYPE(c) == EQUAL) - c = CHILD(n, j += 3); - if (TYPE(CHILD(c, 0)) == LPAR) - symtable_params_fplist(st, CHILD(c, 1)); - } + case Index_kind: + if (ctx != AugStore) + VISIT(c, expr, s->v.Index.value); + return compiler_handle_subscr(c, "index", ctx); } + return 1; } +/* do depth-first search of basic block graph, starting with block. + post records the block indices in post-order. + + XXX must handle implicit jumps from one block to next +*/ + static void -symtable_params_fplist(struct symtable *st, node *n) +dfs(struct compiler *c, basicblock *b, struct assembler *a) { int i; - node *c; - - REQ(n, fplist); - for (i = 0; i < NCH(n); i += 2) { - c = CHILD(n, i); - REQ(c, fpdef); - if (NCH(c) == 1) - symtable_add_def(st, STR(CHILD(c, 0)), - DEF_PARAM | DEF_INTUPLE); - else - symtable_params_fplist(st, CHILD(c, 1)); + struct instr *instr = NULL; + + if (b->b_seen) + return; + b->b_seen = 1; + if (b->b_next != NULL) + dfs(c, b->b_next, a); + for (i = 0; i < b->b_iused; i++) { + instr = &b->b_instr[i]; + if (instr->i_jrel || instr->i_jabs) + dfs(c, instr->i_target, a); } - + a->a_postorder[a->a_nblocks++] = b; } -static void -symtable_global(struct symtable *st, node *n) +int +stackdepth_walk(struct compiler *c, basicblock *b, int depth, int maxdepth) { int i; - - /* XXX It might be helpful to warn about module-level global - statements, but it's hard to tell the difference between - module-level and a string passed to exec. - */ - - for (i = 1; i < NCH(n); i += 2) { - char *name = STR(CHILD(n, i)); - int flags; - - flags = symtable_lookup(st, name); - if (flags < 0) - continue; - if (flags && flags != DEF_GLOBAL) { - char buf[500]; - if (flags & DEF_PARAM) { - PyErr_Format(PyExc_SyntaxError, PARAM_GLOBAL, - name); - symtable_error(st, 0); - return; - } - else { - if (flags & DEF_LOCAL) - PyOS_snprintf(buf, sizeof(buf), - GLOBAL_AFTER_ASSIGN, - name); - else - PyOS_snprintf(buf, sizeof(buf), - GLOBAL_AFTER_USE, name); - symtable_warn(st, buf); + struct instr *instr; + if (b->b_seen || b->b_startdepth >= depth) + return maxdepth; + b->b_seen = 1; + b->b_startdepth = depth; + for (i = 0; i < b->b_iused; i++) { + instr = &b->b_instr[i]; + depth += opcode_stack_effect(instr->i_opcode, instr->i_oparg); + if (depth > maxdepth) + maxdepth = depth; + assert(depth >= 0); /* invalid code or bug in stackdepth() */ + if (instr->i_jrel || instr->i_jabs) { + maxdepth = stackdepth_walk(c, instr->i_target, + depth, maxdepth); + if (instr->i_opcode == JUMP_ABSOLUTE || + instr->i_opcode == JUMP_FORWARD) { + goto out; /* remaining code is dead */ } } - symtable_add_def(st, name, DEF_GLOBAL); } + if (b->b_next) + maxdepth = stackdepth_walk(c, b->b_next, depth, maxdepth); +out: + b->b_seen = 0; + return maxdepth; } -static void -symtable_list_comprehension(struct symtable *st, node *n) +/* Find the flow path that needs the largest stack. We assume that + * cycles in the flow graph have no net effect on the stack depth. + */ +static int +stackdepth(struct compiler *c) { - /* listmaker: test list_for */ - char tmpname[30]; - - REQ(n, listmaker); - PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", - ++st->st_cur->ste_tmpname); - symtable_add_def(st, tmpname, DEF_LOCAL); - symtable_list_for(st, CHILD(n, 1)); - symtable_node(st, CHILD(n, 0)); - --st->st_cur->ste_tmpname; + basicblock *b, *entryblock; + entryblock = NULL; + for (b = c->u->u_blocks; b != NULL; b = b->b_list) { + b->b_seen = 0; + b->b_startdepth = INT_MIN; + entryblock = b; + } + return stackdepth_walk(c, entryblock, 0, 0); } -static void -symtable_generator_expression(struct symtable *st, node *n) +static int +assemble_init(struct assembler *a, int nblocks, int firstlineno) { - /* testlist_gexp: test gen_for */ - REQ(CHILD(n, 0), test); - REQ(CHILD(n, 1), gen_for); + memset(a, 0, sizeof(struct assembler)); + a->a_lineno = firstlineno; + a->a_bytecode = PyString_FromStringAndSize(NULL, DEFAULT_CODE_SIZE); + if (!a->a_bytecode) + return 0; + a->a_lnotab = PyString_FromStringAndSize(NULL, DEFAULT_LNOTAB_SIZE); + if (!a->a_lnotab) + return 0; + a->a_postorder = (basicblock **)PyObject_Malloc( + sizeof(basicblock *) * nblocks); + if (!a->a_postorder) + return 0; + return 1; +} - symtable_enter_scope(st, "", TYPE(n), n->n_lineno); - st->st_cur->ste_generator = GENERATOR_EXPRESSION; +static void +assemble_free(struct assembler *a) +{ + Py_XDECREF(a->a_bytecode); + Py_XDECREF(a->a_lnotab); + if (a->a_postorder) + PyObject_Free(a->a_postorder); +} - symtable_add_def(st, "[outmost-iterable]", DEF_PARAM); - - symtable_gen_for(st, CHILD(n, 1), 1); - symtable_node(st, CHILD(n, 0)); - symtable_exit_scope(st); +/* Return the size of a basic block in bytes. */ - /* for outmost iterable precomputation */ - symtable_node(st, CHILD(CHILD(n, 1), 3)); +static int +instrsize(struct instr *instr) +{ + int size = 1; + if (instr->i_hasarg) { + size += 2; + if (instr->i_oparg >> 16) + size += 2; + } + return size; } -static void -symtable_list_for(struct symtable *st, node *n) +static int +blocksize(basicblock *b) { - REQ(n, list_for); - /* list_for: for v in expr [list_iter] */ - symtable_assign(st, CHILD(n, 1), 0); - symtable_node(st, CHILD(n, 3)); - if (NCH(n) == 5) - symtable_node(st, CHILD(n, 4)); + int i; + int size = 0; + + for (i = 0; i < b->b_iused; i++) + size += instrsize(&b->b_instr[i]); + return size; } -static void -symtable_gen_for(struct symtable *st, node *n, int is_outmost) +/* All about a_lnotab. + +c_lnotab is an array of unsigned bytes disguised as a Python string. +It is used to map bytecode offsets to source code line #s (when needed +for tracebacks). + +The array is conceptually a list of + (bytecode offset increment, line number increment) +pairs. The details are important and delicate, best illustrated by example: + + byte code offset source code line number + 0 1 + 6 2 + 50 7 + 350 307 + 361 308 + +The first trick is that these numbers aren't stored, only the increments +from one row to the next (this doesn't really work, but it's a start): + + 0, 1, 6, 1, 44, 5, 300, 300, 11, 1 + +The second trick is that an unsigned byte can't hold negative values, or +values larger than 255, so (a) there's a deep assumption that byte code +offsets and their corresponding line #s both increase monotonically, and (b) +if at least one column jumps by more than 255 from one row to the next, more +than one pair is written to the table. In case #b, there's no way to know +from looking at the table later how many were written. That's the delicate +part. A user of c_lnotab desiring to find the source line number +corresponding to a bytecode address A should do something like this + + lineno = addr = 0 + for addr_incr, line_incr in c_lnotab: + addr += addr_incr + if addr > A: + return lineno + lineno += line_incr + +In order for this to work, when the addr field increments by more than 255, +the line # increment in each pair generated must be 0 until the remaining addr +increment is < 256. So, in the example above, com_set_lineno should not (as +was actually done until 2.2) expand 300, 300 to 255, 255, 45, 45, but to +255, 0, 45, 255, 0, 45. +*/ + +static int +assemble_lnotab(struct assembler *a, struct instr *i) { - REQ(n, gen_for); + int d_bytecode, d_lineno; + int len; + char *lnotab; - /* gen_for: for v in test [gen_iter] */ - symtable_assign(st, CHILD(n, 1), 0); - if (is_outmost) - symtable_add_use(st, "[outmost-iterable]"); - else - symtable_node(st, CHILD(n, 3)); + d_bytecode = a->a_offset - a->a_lineno_off; + d_lineno = i->i_lineno - a->a_lineno; + + assert(d_bytecode >= 0); + assert(d_lineno >= 0); + + if (d_lineno == 0) + return 1; + + if (d_bytecode > 255) { + int i, nbytes, ncodes = d_bytecode / 255; + nbytes = a->a_lnotab_off + 2 * ncodes; + len = PyString_GET_SIZE(a->a_lnotab); + if (nbytes >= len) { + if (len * 2 < nbytes) + len = nbytes; + else + len *= 2; + if (_PyString_Resize(&a->a_lnotab, len) < 0) + return 0; + } + lnotab = PyString_AS_STRING(a->a_lnotab) + a->a_lnotab_off; + for (i = 0; i < ncodes; i++) { + *lnotab++ = 255; + *lnotab++ = 0; + } + d_bytecode -= ncodes * 255; + a->a_lnotab_off += ncodes * 2; + } + assert(d_bytecode <= 255); + if (d_lineno > 255) { + int i, nbytes, ncodes = d_lineno / 255; + nbytes = a->a_lnotab_off + 2 * ncodes; + len = PyString_GET_SIZE(a->a_lnotab); + if (nbytes >= len) { + if (len * 2 < nbytes) + len = nbytes; + else + len *= 2; + if (_PyString_Resize(&a->a_lnotab, len) < 0) + return 0; + } + lnotab = PyString_AS_STRING(a->a_lnotab) + a->a_lnotab_off; + *lnotab++ = 255; + *lnotab++ = d_bytecode; + d_bytecode = 0; + for (i = 1; i < ncodes; i++) { + *lnotab++ = 255; + *lnotab++ = 0; + } + d_lineno -= ncodes * 255; + a->a_lnotab_off += ncodes * 2; + } - if (NCH(n) == 5) - symtable_gen_iter(st, CHILD(n, 4)); + len = PyString_GET_SIZE(a->a_lnotab); + if (a->a_lnotab_off + 2 >= len) { + if (_PyString_Resize(&a->a_lnotab, len * 2) < 0) + return 0; + } + lnotab = PyString_AS_STRING(a->a_lnotab) + a->a_lnotab_off; + + a->a_lnotab_off += 2; + if (d_bytecode) { + *lnotab++ = d_bytecode; + *lnotab++ = d_lineno; + } + else { /* First line of a block; def stmt, etc. */ + *lnotab++ = 0; + *lnotab++ = d_lineno; + } + a->a_lineno = i->i_lineno; + a->a_lineno_off = a->a_offset; + return 1; } -static void -symtable_gen_iter(struct symtable *st, node *n) +/* assemble_emit() + Extend the bytecode with a new instruction. + Update lnotab if necessary. +*/ + +static int +assemble_emit(struct assembler *a, struct instr *i) { - REQ(n, gen_iter); + int arg = 0, size = 0, ext = i->i_oparg >> 16; + int len = PyString_GET_SIZE(a->a_bytecode); + char *code; - n = CHILD(n, 0); - if (TYPE(n) == gen_for) - symtable_gen_for(st, n, 0); + if (!i->i_hasarg) + size = 1; else { - REQ(n, gen_if); - symtable_node(st, CHILD(n, 1)); - - if (NCH(n) == 3) - symtable_gen_iter(st, CHILD(n, 2)); + if (ext) + size = 6; + else + size = 3; + arg = i->i_oparg; } + if (i->i_lineno && !assemble_lnotab(a, i)) + return 0; + if (a->a_offset + size >= len) { + if (_PyString_Resize(&a->a_bytecode, len * 2) < 0) + return 0; + } + code = PyString_AS_STRING(a->a_bytecode) + a->a_offset; + a->a_offset += size; + if (ext > 0) { + *code++ = (char)EXTENDED_ARG; + *code++ = ext & 0xff; + *code++ = ext >> 8; + arg &= 0xffff; + } + *code++ = i->i_opcode; + if (size == 1) + return 1; + *code++ = arg & 0xff; + *code++ = arg >> 8; + return 1; } -static void -symtable_import(struct symtable *st, node *n) +static int +assemble_jump_offsets(struct assembler *a, struct compiler *c) { - node *nn; + basicblock *b; + int bsize, totsize = 0; int i; - /* import_stmt: import_name | import_from */ - n = CHILD(n, 0); - if (TYPE(n) == import_from) { - /* import_from: 'from' dotted_name 'import' ('*' | - | '(' import_as_names ')' | import_as_names) */ - node *dotname = CHILD(n, 1); - REQ(dotname, dotted_name); - if (strcmp(STR(CHILD(dotname, 0)), "__future__") == 0) { - /* check for bogus imports */ - if (n->n_lineno >= st->st_future->ff_last_lineno) { - PyErr_SetString(PyExc_SyntaxError, - LATE_FUTURE); - symtable_error(st, n->n_lineno); - return; - } - } - nn = CHILD(n, 3 + (TYPE(CHILD(n, 3)) == LPAR)); - if (TYPE(nn) == STAR) { - if (st->st_cur->ste_type != TYPE_MODULE) { - if (symtable_warn(st, - "import * only allowed at module level") < 0) - return; - } - st->st_cur->ste_optimized |= OPT_IMPORT_STAR; - st->st_cur->ste_opt_lineno = n->n_lineno; - } else { - REQ(nn, import_as_names); - for (i = 0; i < NCH(nn); i += 2) { - node *c = CHILD(nn, i); - if (NCH(c) > 1) /* import as */ - symtable_assign(st, CHILD(c, 2), - DEF_IMPORT); - else - symtable_assign(st, CHILD(c, 0), - DEF_IMPORT); + + /* Compute the size of each block and fixup jump args. + Replace block pointer with position in bytecode. */ + for (i = a->a_nblocks - 1; i >= 0; i--) { + basicblock *b = a->a_postorder[i]; + bsize = blocksize(b); + b->b_offset = totsize; + totsize += bsize; + } + for (b = c->u->u_blocks; b != NULL; b = b->b_list) { + bsize = b->b_offset; + for (i = 0; i < b->b_iused; i++) { + struct instr *instr = &b->b_instr[i]; + /* Relative jumps are computed relative to + the instruction pointer after fetching + the jump instruction. + */ + bsize += instrsize(instr); + if (instr->i_jabs) + instr->i_oparg = instr->i_target->b_offset; + else if (instr->i_jrel) { + int delta = instr->i_target->b_offset - bsize; + instr->i_oparg = delta; } } - } else { - /* 'import' dotted_as_names */ - nn = CHILD(n, 1); - REQ(nn, dotted_as_names); - for (i = 0; i < NCH(nn); i += 2) - symtable_assign(st, CHILD(nn, i), DEF_IMPORT); } + return 1; } -/* The third argument to symatble_assign() is a flag to be passed to - symtable_add_def() if it is eventually called. The flag is useful - to specify the particular type of assignment that should be - recorded, e.g. an assignment caused by import. - */ +static PyObject * +dict_keys_inorder(PyObject *dict, int offset) +{ + PyObject *tuple, *k, *v; + int i, pos = 0, size = PyDict_Size(dict); + + tuple = PyTuple_New(size); + if (tuple == NULL) + return NULL; + while (PyDict_Next(dict, &pos, &k, &v)) { + i = PyInt_AS_LONG(v); + k = PyTuple_GET_ITEM(k, 0); + Py_INCREF(k); + assert((i - offset) < size); + assert((i - offset) >= 0); + PyTuple_SET_ITEM(tuple, i - offset, k); + } + return tuple; +} + +static int +compute_code_flags(struct compiler *c) +{ + PySTEntryObject *ste = c->u->u_ste; + int flags = 0, n; + if (ste->ste_type != ModuleBlock) + flags |= CO_NEWLOCALS; + if (ste->ste_type == FunctionBlock) { + if (!ste->ste_unoptimized) + flags |= CO_OPTIMIZED; + if (ste->ste_nested) + flags |= CO_NESTED; + if (ste->ste_generator) + flags |= CO_GENERATOR; + } + if (ste->ste_varargs) + flags |= CO_VARARGS; + if (ste->ste_varkeywords) + flags |= CO_VARKEYWORDS; + if (ste->ste_generator) + flags |= CO_GENERATOR; + if (c->c_flags->cf_flags & CO_FUTURE_DIVISION) + flags |= CO_FUTURE_DIVISION; + n = PyDict_Size(c->u->u_freevars); + if (n < 0) + return -1; + if (n == 0) { + n = PyDict_Size(c->u->u_cellvars); + if (n < 0) + return -1; + if (n == 0) { + flags |= CO_NOFREE; + } + } + + return flags; +} + +static PyCodeObject * +makecode(struct compiler *c, struct assembler *a) +{ + PyObject *tmp; + PyCodeObject *co = NULL; + PyObject *consts = NULL; + PyObject *names = NULL; + PyObject *varnames = NULL; + PyObject *filename = NULL; + PyObject *name = NULL; + PyObject *freevars = NULL; + PyObject *cellvars = NULL; + PyObject *bytecode = NULL; + int nlocals, flags; + + tmp = dict_keys_inorder(c->u->u_consts, 0); + if (!tmp) + goto error; + consts = PySequence_List(tmp); /* optimize_code requires a list */ + Py_DECREF(tmp); + + names = dict_keys_inorder(c->u->u_names, 0); + varnames = dict_keys_inorder(c->u->u_varnames, 0); + if (!consts || !names || !varnames) + goto error; + + cellvars = dict_keys_inorder(c->u->u_cellvars, 0); + if (!cellvars) + goto error; + freevars = dict_keys_inorder(c->u->u_freevars, PyTuple_Size(cellvars)); + if (!freevars) + goto error; + filename = PyString_FromString(c->c_filename); + if (!filename) + goto error; + + nlocals = PyDict_Size(c->u->u_varnames); + flags = compute_code_flags(c); + if (flags < 0) + goto error; + + bytecode = optimize_code(a->a_bytecode, consts, names, a->a_lnotab); + if (!bytecode) + goto error; + + tmp = PyList_AsTuple(consts); /* PyCode_New requires a tuple */ + if (!tmp) + goto error; + Py_DECREF(consts); + consts = tmp; + + co = PyCode_New(c->u->u_argcount, nlocals, stackdepth(c), flags, + bytecode, consts, names, varnames, + freevars, cellvars, + filename, c->u->u_name, + c->u->u_firstlineno, + a->a_lnotab); + error: + Py_XDECREF(consts); + Py_XDECREF(names); + Py_XDECREF(varnames); + Py_XDECREF(filename); + Py_XDECREF(name); + Py_XDECREF(freevars); + Py_XDECREF(cellvars); + Py_XDECREF(bytecode); + return co; +} -static void -symtable_assign(struct symtable *st, node *n, int def_flag) +static PyCodeObject * +assemble(struct compiler *c, int addNone) { - node *tmp; - int i; + basicblock *b, *entryblock; + struct assembler a; + int i, j, nblocks; + PyCodeObject *co = NULL; - loop: - switch (TYPE(n)) { - case lambdef: - /* invalid assignment, e.g. lambda x:x=2. The next - pass will catch this error. */ - return; - case power: - if (NCH(n) > 2) { - for (i = 2; i < NCH(n); ++i) - if (TYPE(CHILD(n, i)) != DOUBLESTAR) - symtable_node(st, CHILD(n, i)); - } - if (NCH(n) > 1) { - symtable_node(st, CHILD(n, 0)); - symtable_node(st, CHILD(n, 1)); - } else { - n = CHILD(n, 0); - goto loop; - } - return; - case listmaker: - if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) { - /* XXX This is an error, but the next pass - will catch it. */ - return; - } else { - for (i = 0; i < NCH(n); i += 2) - symtable_assign(st, CHILD(n, i), def_flag); - } - return; - case testlist_gexp: - if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == gen_for) { - /* XXX This is an error, but the next pass - will catch it. */ - return; - } else { - for (i = 0; i < NCH(n); i += 2) - symtable_assign(st, CHILD(n, i), def_flag); - } - return; + /* Make sure every block that falls off the end returns None. + XXX NEXT_BLOCK() isn't quite right, because if the last + block ends with a jump or return b_next shouldn't set. + */ + if (!c->u->u_curblock->b_return) { + NEXT_BLOCK(c); + if (addNone) + ADDOP_O(c, LOAD_CONST, Py_None, consts); + ADDOP(c, RETURN_VALUE); + } - case exprlist: - case testlist: - case testlist1: - if (NCH(n) == 1) { - n = CHILD(n, 0); - goto loop; - } - else { - int i; - for (i = 0; i < NCH(n); i += 2) - symtable_assign(st, CHILD(n, i), def_flag); - return; - } - case atom: - tmp = CHILD(n, 0); - if (TYPE(tmp) == LPAR || TYPE(tmp) == LSQB) { - n = CHILD(n, 1); - goto loop; - } else if (TYPE(tmp) == NAME) { - if (strcmp(STR(tmp), "__debug__") == 0) { - PyErr_SetString(PyExc_SyntaxError, - ASSIGN_DEBUG); - symtable_error(st, n->n_lineno); - return; - } - symtable_add_def(st, STR(tmp), DEF_LOCAL | def_flag); - } - return; + nblocks = 0; + entryblock = NULL; + for (b = c->u->u_blocks; b != NULL; b = b->b_list) { + nblocks++; + entryblock = b; + } - case yield_expr: - st->st_cur->ste_generator = 1; - if (NCH(n)==2) { - n = CHILD(n, 1); - goto loop; - } - return; + if (!assemble_init(&a, nblocks, c->u->u_firstlineno)) + goto error; + dfs(c, entryblock, &a); - case dotted_as_name: - if (NCH(n) == 3) - symtable_add_def(st, STR(CHILD(n, 2)), - DEF_LOCAL | def_flag); - else - symtable_add_def(st, - STR(CHILD(CHILD(n, - 0), 0)), - DEF_LOCAL | def_flag); - return; - case dotted_name: - symtable_add_def(st, STR(CHILD(n, 0)), DEF_LOCAL | def_flag); - return; - case NAME: - symtable_add_def(st, STR(n), DEF_LOCAL | def_flag); - return; - default: - if (NCH(n) == 0) - return; - if (NCH(n) == 1) { - n = CHILD(n, 0); - goto loop; - } - /* Should only occur for errors like x + 1 = 1, - which will be caught in the next pass. */ - for (i = 0; i < NCH(n); ++i) - if (TYPE(CHILD(n, i)) >= single_input) - symtable_assign(st, CHILD(n, i), def_flag); + /* Can't modify the bytecode after computing jump offsets. */ + if (!assemble_jump_offsets(&a, c)) + goto error; + + /* Emit code in reverse postorder from dfs. */ + for (i = a.a_nblocks - 1; i >= 0; i--) { + basicblock *b = a.a_postorder[i]; + for (j = 0; j < b->b_iused; j++) + if (!assemble_emit(&a, &b->b_instr[j])) + goto error; } + + if (_PyString_Resize(&a.a_lnotab, a.a_lnotab_off) < 0) + goto error; + if (_PyString_Resize(&a.a_bytecode, a.a_offset) < 0) + goto error; + + co = makecode(c, &a); + error: + assemble_free(&a); + return co; } diff --git a/Python/future.c b/Python/future.c index 95d6a5c..a0cfeac 100644 --- a/Python/future.c +++ b/Python/future.c @@ -1,37 +1,30 @@ #include "Python.h" +#include "Python-ast.h" #include "node.h" #include "token.h" #include "graminit.h" +#include "code.h" #include "compile.h" #include "symtable.h" #define UNDEFINED_FUTURE_FEATURE "future feature %.100s is not defined" #define FUTURE_IMPORT_STAR "future statement does not support import *" -/* FUTURE_POSSIBLE() is provided to accomodate doc strings, which is - the only statement that can occur before a future statement. -*/ -#define FUTURE_POSSIBLE(FF) ((FF)->ff_last_lineno == -1) - static int -future_check_features(PyFutureFeatures *ff, node *n, const char *filename) +future_check_features(PyFutureFeatures *ff, stmt_ty s, const char *filename) { int i; - char *feature; - node *ch, *nn; + const char *feature; + asdl_seq *names; - REQ(n, import_from); - nn = CHILD(n, 3 + (TYPE(CHILD(n, 3)) == LPAR)); - if (TYPE(nn) == STAR) { - PyErr_SetString(PyExc_SyntaxError, FUTURE_IMPORT_STAR); - PyErr_SyntaxLocation(filename, nn->n_lineno); - return -1; - } - REQ(nn, import_as_names); - for (i = 0; i < NCH(nn); i += 2) { - ch = CHILD(nn, i); - REQ(ch, import_as_name); - feature = STR(CHILD(ch, 0)); + assert(s->kind == ImportFrom_kind); + + names = s->v.ImportFrom.names; + for (i = 0; i < asdl_seq_LEN(names); i++) { + alias_ty name = asdl_seq_GET(names, i); + feature = PyString_AsString(name->name); + if (!feature) + return 0; if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) { continue; } else if (strcmp(feature, FUTURE_GENERATORS) == 0) { @@ -41,218 +34,97 @@ future_check_features(PyFutureFeatures *ff, node *n, const char *filename) } else if (strcmp(feature, "braces") == 0) { PyErr_SetString(PyExc_SyntaxError, "not a chance"); - PyErr_SyntaxLocation(filename, CHILD(ch, 0)->n_lineno); - return -1; + PyErr_SyntaxLocation(filename, s->lineno); + return 0; } else { PyErr_Format(PyExc_SyntaxError, UNDEFINED_FUTURE_FEATURE, feature); - PyErr_SyntaxLocation(filename, CHILD(ch, 0)->n_lineno); - return -1; + PyErr_SyntaxLocation(filename, s->lineno); + return 0; } } - return 0; + return 1; } -static void -future_error(node *n, const char *filename) +int +future_parse(PyFutureFeatures *ff, mod_ty mod, const char *filename) { - PyErr_SetString(PyExc_SyntaxError, - "from __future__ imports must occur at the " - "beginning of the file"); - PyErr_SyntaxLocation(filename, n->n_lineno); -} - -/* Relevant portions of the grammar: - -single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE -file_input: (NEWLINE | stmt)* ENDMARKER -stmt: simple_stmt | compound_stmt -simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE -small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt - | import_stmt | global_stmt | exec_stmt | assert_stmt -import_stmt: 'import' dotted_as_name (',' dotted_as_name)* - | 'from' dotted_name 'import' ('*' | import_as_name (',' import_as_name)*) -import_as_name: NAME [NAME NAME] -dotted_as_name: dotted_name [NAME NAME] -dotted_name: NAME ('.' NAME)* -*/ - -/* future_parse() finds future statements at the beginnning of a - module. The function calls itself recursively, rather than - factoring out logic for different kinds of statements into - different routines. - - Return values: - -1 indicates an error occurred, e.g. unknown feature name - 0 indicates no feature was found - 1 indicates a feature was found -*/ + int i, found_docstring = 0, done = 0, prev_line = 0; -static int -future_parse(PyFutureFeatures *ff, node *n, const char *filename) -{ - int i, r; - loop: - switch (TYPE(n)) { + static PyObject *future; + if (!future) { + future = PyString_InternFromString("__future__"); + if (!future) + return 0; + } - case single_input: - if (TYPE(CHILD(n, 0)) == simple_stmt) { - n = CHILD(n, 0); - goto loop; - } - return 0; + if (!(mod->kind == Module_kind || mod->kind == Interactive_kind)) + return 1; - case file_input: - /* Check each statement in the file, starting with the - first, and continuing until the first statement - that isn't a future statement. + /* A subsequent pass will detect future imports that don't + appear at the beginning of the file. There's one case, + however, that is easier to handl here: A series of imports + joined by semi-colons, where the first import is a future + statement but some subsequent import has the future form + but is preceded by a regular import. + */ + + + for (i = 0; i < asdl_seq_LEN(mod->v.Module.body); i++) { + stmt_ty s = asdl_seq_GET(mod->v.Module.body, i); + + if (done && s->lineno > prev_line) + return 1; + prev_line = s->lineno; + + /* The tests below will return from this function unless it is + still possible to find a future statement. The only things + that can precede a future statement are another future + statement and a doc string. */ - for (i = 0; i < NCH(n); i++) { - node *ch = CHILD(n, i); - if (TYPE(ch) == stmt) { - r = future_parse(ff, ch, filename); - /* Need to check both conditions below - to accomodate doc strings, which - causes r < 0. - */ - if (r < 1 && !FUTURE_POSSIBLE(ff)) - return r; - } - } - return 0; - - case simple_stmt: - if (NCH(n) == 2) { - REQ(CHILD(n, 0), small_stmt); - n = CHILD(n, 0); - goto loop; - } else { - /* Deal with the special case of a series of - small statements on a single line. If a - future statement follows some other - statement, the SyntaxError is raised here. - In all other cases, the symtable pass - raises the exception. - */ - int found = 0, end_of_future = 0; - for (i = 0; i < NCH(n); i += 2) { - if (TYPE(CHILD(n, i)) == small_stmt) { - r = future_parse(ff, CHILD(n, i), - filename); - if (r < 1) - end_of_future = 1; - else { - found = 1; - if (end_of_future) { - future_error(n, - filename); - return -1; - } - } + if (s->kind == ImportFrom_kind) { + if (s->v.ImportFrom.module == future) { + if (done) { + PyErr_SetString(PyExc_SyntaxError, + ERR_LATE_FUTURE); + PyErr_SyntaxLocation(filename, + s->lineno); + return 0; } + if (!future_check_features(ff, s, filename)) + return 0; + ff->ff_lineno = s->lineno; } - - /* If we found one and only one, then the - current lineno is legal. - */ - if (found) - ff->ff_last_lineno = n->n_lineno + 1; else - ff->ff_last_lineno = n->n_lineno; - - if (end_of_future && found) - return 1; - else - return 0; - } - - case stmt: - if (TYPE(CHILD(n, 0)) == simple_stmt) { - n = CHILD(n, 0); - goto loop; - } else if (TYPE(CHILD(n, 0)) == expr_stmt) { - n = CHILD(n, 0); - goto loop; - } else { - REQ(CHILD(n, 0), compound_stmt); - ff->ff_last_lineno = n->n_lineno; - return 0; - } - - case small_stmt: - n = CHILD(n, 0); - goto loop; - - case import_stmt: { - node *name; - - n = CHILD(n, 0); - if (TYPE(n) != import_from) { - ff->ff_last_lineno = n->n_lineno; - return 0; + done = 1; } - name = CHILD(n, 1); - if (strcmp(STR(CHILD(name, 0)), "__future__") != 0) - return 0; - if (future_check_features(ff, n, filename) < 0) - return -1; - ff->ff_last_lineno = n->n_lineno + 1; - return 1; - } - - /* The cases below -- all of them! -- are necessary to find - and skip doc strings. */ - case expr_stmt: - case testlist: - case test: - case and_test: - case not_test: - case comparison: - case expr: - case xor_expr: - case and_expr: - case shift_expr: - case arith_expr: - case term: - case factor: - case power: - if (NCH(n) == 1) { - n = CHILD(n, 0); - goto loop; - } - ff->ff_last_lineno = n->n_lineno; - break; - - case atom: - if (TYPE(CHILD(n, 0)) == STRING - && ff->ff_found_docstring == 0) { - ff->ff_found_docstring = 1; - return 0; + else if (s->kind == Expr_kind && !found_docstring) { + expr_ty e = s->v.Expr.value; + if (e->kind != Str_kind) + done = 1; + else + found_docstring = 1; } - ff->ff_last_lineno = n->n_lineno; - return 0; - - default: - ff->ff_last_lineno = n->n_lineno; - return 0; + else + done = 1; } - return 0; + return 1; } + PyFutureFeatures * -PyNode_Future(node *n, const char *filename) +PyFuture_FromAST(mod_ty mod, const char *filename) { PyFutureFeatures *ff; ff = (PyFutureFeatures *)PyMem_Malloc(sizeof(PyFutureFeatures)); if (ff == NULL) return NULL; - ff->ff_found_docstring = 0; - ff->ff_last_lineno = -1; ff->ff_features = 0; + ff->ff_lineno = -1; - if (future_parse(ff, n, filename) < 0) { + if (!future_parse(ff, mod, filename)) { PyMem_Free((void *)ff); return NULL; } diff --git a/Python/import.c b/Python/import.c index 35de13e..dcbca38 100644 --- a/Python/import.c +++ b/Python/import.c @@ -3,10 +3,11 @@ #include "Python.h" -#include "node.h" -#include "token.h" +#include "Python-ast.h" +#include "pythonrun.h" #include "errcode.h" #include "marshal.h" +#include "code.h" #include "compile.h" #include "eval.h" #include "osdefs.h" @@ -766,17 +767,17 @@ load_compiled_module(char *name, char *cpathname, FILE *fp) /* Parse a source file and return the corresponding code object */ static PyCodeObject * -parse_source_module(char *pathname, FILE *fp) +parse_source_module(const char *pathname, FILE *fp) { - PyCodeObject *co; - node *n; - - n = PyParser_SimpleParseFile(fp, pathname, Py_file_input); - if (n == NULL) - return NULL; - co = PyNode_Compile(n, pathname); - PyNode_Free(n); + PyCodeObject *co = NULL; + mod_ty mod; + mod = PyParser_ASTFromFile(fp, pathname, Py_file_input, 0, 0, 0, + NULL); + if (mod) { + co = PyAST_Compile(mod, pathname, NULL); + free_mod(mod); + } return co; } diff --git a/Python/marshal.c b/Python/marshal.c index 20d637d..4114c8e 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -6,6 +6,7 @@ #include "Python.h" #include "longintrepr.h" +#include "code.h" #include "compile.h" #include "marshal.h" diff --git a/Python/pythonrun.c b/Python/pythonrun.c index e007f98..ad837d2 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -3,13 +3,16 @@ #include "Python.h" +#include "Python-ast.h" #include "grammar.h" #include "node.h" #include "token.h" #include "parsetok.h" #include "errcode.h" +#include "code.h" #include "compile.h" #include "symtable.h" +#include "ast.h" #include "eval.h" #include "marshal.h" @@ -32,9 +35,9 @@ extern grammar _PyParser_Grammar; /* From graminit.c */ /* Forward */ static void initmain(void); static void initsite(void); -static PyObject *run_err_node(node *, const char *, PyObject *, PyObject *, +static PyObject *run_err_mod(mod_ty, const char *, PyObject *, PyObject *, PyCompilerFlags *); -static PyObject *run_node(node *, const char *, PyObject *, PyObject *, +static PyObject *run_mod(mod_ty, const char *, PyObject *, PyObject *, PyCompilerFlags *); static PyObject *run_pyc_file(FILE *, const char *, PyObject *, PyObject *, PyCompilerFlags *); @@ -634,25 +637,7 @@ initsite(void) /* Parse input from a file and execute it */ int -PyRun_AnyFile(FILE *fp, const char *filename) -{ - return PyRun_AnyFileExFlags(fp, filename, 0, NULL); -} - -int -PyRun_AnyFileFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) -{ - return PyRun_AnyFileExFlags(fp, filename, 0, flags); -} - -int -PyRun_AnyFileEx(FILE *fp, const char *filename, int closeit) -{ - return PyRun_AnyFileExFlags(fp, filename, closeit, NULL); -} - -int -PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit, +PyRun_AnyFileExFlags(FILE *fp, char *filename, int closeit, PyCompilerFlags *flags) { if (filename == NULL) @@ -668,12 +653,6 @@ PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit, } int -PyRun_InteractiveLoop(FILE *fp, const char *filename) -{ - return PyRun_InteractiveLoopFlags(fp, filename, NULL); -} - -int PyRun_InteractiveLoopFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) { PyObject *v; @@ -708,12 +687,6 @@ PyRun_InteractiveLoopFlags(FILE *fp, const char *filename, PyCompilerFlags *flag } } -int -PyRun_InteractiveOne(FILE *fp, const char *filename) -{ - return PyRun_InteractiveOneFlags(fp, filename, NULL); -} - /* compute parser flags based on compiler flags */ #define PARSER_FLAGS(flags) \ (((flags) && (flags)->cf_flags & PyCF_DONT_IMPLY_DEDENT) ? \ @@ -723,9 +696,9 @@ int PyRun_InteractiveOneFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) { PyObject *m, *d, *v, *w; - node *n; - perrdetail err; + mod_ty mod; char *ps1 = "", *ps2 = ""; + int errcode = 0; v = PySys_GetObject("ps1"); if (v != NULL) { @@ -743,26 +716,25 @@ PyRun_InteractiveOneFlags(FILE *fp, const char *filename, PyCompilerFlags *flags else if (PyString_Check(w)) ps2 = PyString_AsString(w); } - n = PyParser_ParseFileFlags(fp, filename, &_PyParser_Grammar, - Py_single_input, ps1, ps2, &err, - PARSER_FLAGS(flags)); + mod = PyParser_ASTFromFile(fp, filename, + Py_single_input, ps1, ps2, + flags, &errcode); Py_XDECREF(v); Py_XDECREF(w); - if (n == NULL) { - if (err.error == E_EOF) { - if (err.text) - PyMem_DEL(err.text); + if (mod == NULL) { + if (errcode == E_EOF) { + PyErr_Clear(); return E_EOF; } - err_input(&err); PyErr_Print(); - return err.error; + return -1; } m = PyImport_AddModule("__main__"); if (m == NULL) return -1; d = PyModule_GetDict(m); - v = run_node(n, filename, d, d, flags); + v = run_mod(mod, filename, d, d, flags); + free_mod(mod); if (v == NULL) { PyErr_Print(); return -1; @@ -773,12 +745,6 @@ PyRun_InteractiveOneFlags(FILE *fp, const char *filename, PyCompilerFlags *flags return 0; } -int -PyRun_SimpleFile(FILE *fp, const char *filename) -{ - return PyRun_SimpleFileEx(fp, filename, 0); -} - /* Check whether a file maybe a pyc file: Look at the extension, the file type, and, if we may close it, at the first few bytes. */ @@ -820,12 +786,6 @@ maybe_pyc_file(FILE *fp, const char* filename, const char* ext, int closeit) } int -PyRun_SimpleFileEx(FILE *fp, const char *filename, int closeit) -{ - return PyRun_SimpleFileExFlags(fp, filename, closeit, NULL); -} - -int PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, PyCompilerFlags *flags) { @@ -874,12 +834,6 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, } int -PyRun_SimpleString(const char *command) -{ - return PyRun_SimpleStringFlags(command, NULL); -} - -int PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags) { PyObject *m, *d, *v; @@ -1054,6 +1008,8 @@ PyErr_PrintEx(int set_sys_last_vars) handle_system_exit(); } PyErr_Fetch(&exception, &v, &tb); + if (exception == NULL) + return; PyErr_NormalizeException(&exception, &v, &tb); if (exception == NULL) return; @@ -1195,74 +1151,48 @@ void PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb) } PyObject * -PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals) -{ - return run_err_node(PyParser_SimpleParseString(str, start), - "", globals, locals, NULL); -} - -PyObject * -PyRun_File(FILE *fp, const char *filename, int start, PyObject *globals, - PyObject *locals) +PyRun_StringFlags(const char *str, int start, PyObject *globals, + PyObject *locals, PyCompilerFlags *flags) { - return PyRun_FileEx(fp, filename, start, globals, locals, 0); -} - -PyObject * -PyRun_FileEx(FILE *fp, const char *filename, int start, PyObject *globals, - PyObject *locals, int closeit) -{ - node *n = PyParser_SimpleParseFile(fp, filename, start); - if (closeit) - fclose(fp); - return run_err_node(n, filename, globals, locals, NULL); -} - -PyObject * -PyRun_StringFlags(const char *str, int start, PyObject *globals, PyObject *locals, - PyCompilerFlags *flags) -{ - return run_err_node(PyParser_SimpleParseStringFlags( - str, start, PARSER_FLAGS(flags)), - "", globals, locals, flags); -} - -PyObject * -PyRun_FileFlags(FILE *fp, const char *filename, int start, PyObject *globals, - PyObject *locals, PyCompilerFlags *flags) -{ - return PyRun_FileExFlags(fp, filename, start, globals, locals, 0, - flags); + PyObject *ret; + mod_ty mod = PyParser_ASTFromString(str, "", start, flags); + ret = run_err_mod(mod, "", globals, locals, flags); + free_mod(mod); + return ret; } PyObject * PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals, PyObject *locals, int closeit, PyCompilerFlags *flags) { - node *n = PyParser_SimpleParseFileFlags(fp, filename, start, - PARSER_FLAGS(flags)); + PyObject *ret; + mod_ty mod = PyParser_ASTFromFile(fp, filename, start, 0, 0, + flags, NULL); + if (mod == NULL) + return NULL; if (closeit) fclose(fp); - return run_err_node(n, filename, globals, locals, flags); + ret = run_err_mod(mod, filename, globals, locals, flags); + free_mod(mod); + return ret; } static PyObject * -run_err_node(node *n, const char *filename, PyObject *globals, PyObject *locals, - PyCompilerFlags *flags) +run_err_mod(mod_ty mod, const char *filename, PyObject *globals, + PyObject *locals, PyCompilerFlags *flags) { - if (n == NULL) + if (mod == NULL) return NULL; - return run_node(n, filename, globals, locals, flags); + return run_mod(mod, filename, globals, locals, flags); } static PyObject * -run_node(node *n, const char *filename, PyObject *globals, PyObject *locals, +run_mod(mod_ty mod, const char *filename, PyObject *globals, PyObject *locals, PyCompilerFlags *flags) { PyCodeObject *co; PyObject *v; - co = PyNode_CompileFlags(n, filename, flags); - PyNode_Free(n); + co = PyAST_Compile(mod, filename, flags); if (co == NULL) return NULL; v = PyEval_EvalCode(co, globals, locals); @@ -1271,8 +1201,8 @@ run_node(node *n, const char *filename, PyObject *globals, PyObject *locals, } static PyObject * -run_pyc_file(FILE *fp, const char *filename, PyObject *globals, PyObject *locals, - PyCompilerFlags *flags) +run_pyc_file(FILE *fp, const char *filename, PyObject *globals, + PyObject *locals, PyCompilerFlags *flags) { PyCodeObject *co; PyObject *v; @@ -1303,41 +1233,77 @@ run_pyc_file(FILE *fp, const char *filename, PyObject *globals, PyObject *locals } PyObject * -Py_CompileString(const char *str, const char *filename, int start) -{ - return Py_CompileStringFlags(str, filename, start, NULL); -} - -PyObject * Py_CompileStringFlags(const char *str, const char *filename, int start, PyCompilerFlags *flags) { - node *n; + mod_ty mod; PyCodeObject *co; - - n = PyParser_SimpleParseStringFlagsFilename(str, filename, start, - PARSER_FLAGS(flags)); - if (n == NULL) + mod = PyParser_ASTFromString(str, filename, start, flags); + if (mod == NULL) return NULL; - co = PyNode_CompileFlags(n, filename, flags); - PyNode_Free(n); + co = PyAST_Compile(mod, filename, flags); + free_mod(mod); return (PyObject *)co; } struct symtable * Py_SymtableString(const char *str, const char *filename, int start) { - node *n; + mod_ty mod; struct symtable *st; - n = PyParser_SimpleParseStringFlagsFilename(str, filename, - start, 0); - if (n == NULL) + + mod = PyParser_ASTFromString(str, filename, start, NULL); + if (mod == NULL) return NULL; - st = PyNode_CompileSymtable(n, filename); - PyNode_Free(n); + st = PySymtable_Build(mod, filename, 0); + free_mod(mod); return st; } +/* Preferred access to parser is through AST. */ +mod_ty +PyParser_ASTFromString(const char *s, const char *filename, int start, + PyCompilerFlags *flags) +{ + node *n; + mod_ty mod; + perrdetail err; + n = PyParser_ParseStringFlagsFilename(s, filename, &_PyParser_Grammar, + start, &err, + PARSER_FLAGS(flags)); + if (n) { + mod = PyAST_FromNode(n, flags, filename); + PyNode_Free(n); + return mod; + } + else { + err_input(&err); + return NULL; + } +} + +mod_ty +PyParser_ASTFromFile(FILE *fp, const char *filename, int start, char *ps1, + char *ps2, PyCompilerFlags *flags, int *errcode) +{ + node *n; + mod_ty mod; + perrdetail err; + n = PyParser_ParseFileFlags(fp, filename, &_PyParser_Grammar, start, + ps1, ps2, &err, PARSER_FLAGS(flags)); + if (n) { + mod = PyAST_FromNode(n, flags, filename); + PyNode_Free(n); + return mod; + } + else { + err_input(&err); + if (errcode) + *errcode = err.error; + return NULL; + } +} + /* Simplified interface to parsefile -- return node or set exception */ node * @@ -1349,15 +1315,10 @@ PyParser_SimpleParseFileFlags(FILE *fp, const char *filename, int start, int fla (char *)0, (char *)0, &err, flags); if (n == NULL) err_input(&err); + return n; } -node * -PyParser_SimpleParseFile(FILE *fp, const char *filename, int start) -{ - return PyParser_SimpleParseFileFlags(fp, filename, start, 0); -} - /* Simplified interface to parsestring -- return node or set exception */ node * @@ -1373,12 +1334,6 @@ PyParser_SimpleParseStringFlags(const char *str, int start, int flags) } node * -PyParser_SimpleParseString(const char *str, int start) -{ - return PyParser_SimpleParseStringFlags(str, start, 0); -} - -node * PyParser_SimpleParseStringFlagsFilename(const char *str, const char *filename, int start, int flags) { @@ -1418,12 +1373,6 @@ err_input(perrdetail *err) PyObject* u = NULL; char *msg = NULL; errtype = PyExc_SyntaxError; - v = Py_BuildValue("(ziiz)", err->filename, - err->lineno, err->offset, err->text); - if (err->text != NULL) { - PyMem_DEL(err->text); - err->text = NULL; - } switch (err->error) { case E_SYNTAX: errtype = PyExc_IndentationError; @@ -1450,11 +1399,9 @@ err_input(perrdetail *err) case E_INTR: if (!PyErr_Occurred()) PyErr_SetNone(PyExc_KeyboardInterrupt); - Py_XDECREF(v); return; case E_NOMEM: PyErr_NoMemory(); - Py_XDECREF(v); return; case E_EOF: msg = "unexpected EOF while parsing"; @@ -1498,7 +1445,15 @@ err_input(perrdetail *err) msg = "unknown parsing error"; break; } - w = Py_BuildValue("(sO)", msg, v); + v = Py_BuildValue("(ziiz)", err->filename, + err->lineno, err->offset, err->text); + if (err->text != NULL) { + PyMem_DEL(err->text); + err->text = NULL; + } + w = NULL; + if (v != NULL) + w = Py_BuildValue("(sO)", msg, v); Py_XDECREF(u); Py_XDECREF(v); PyErr_SetObject(errtype, w); @@ -1687,3 +1642,20 @@ PyOS_setsig(int sig, PyOS_sighandler_t handler) return oldhandler; #endif } + +/* Deprecated C API functions still provided for binary compatiblity */ + +#undef PyParser_SimpleParseFile +#undef PyParser_SimpleParseString + +node * +PyParser_SimpleParseFile(FILE *fp, const char *filename, int start) +{ + return PyParser_SimpleParseFileFlags(fp, filename, start, 0); +} + +node * +PyParser_SimpleParseString(const char *str, int start) +{ + return PyParser_SimpleParseStringFlags(str, start, 0); +} diff --git a/Python/symtable.c b/Python/symtable.c index 5ca2041..bd41202 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -1,48 +1,35 @@ #include "Python.h" +#include "Python-ast.h" +#include "code.h" #include "compile.h" #include "symtable.h" -#include "graminit.h" #include "structmember.h" -/* The compiler uses this function to load a PySymtableEntry object - for a code block. Each block is loaded twice, once during the - symbol table pass and once during the code gen pass. Entries - created during the first pass are cached for the second pass, using - the st_symbols dictionary. - - The cache is keyed by st_nscopes. Each code block node in a - module's parse tree can be assigned a unique id based on the order - in which the nodes are visited by the compiler. This strategy - works so long as the symbol table and codegen passes visit the same - nodes in the same order. -*/ +/* two error strings used for warnings */ +#define GLOBAL_AFTER_ASSIGN \ +"name '%.400s' is assigned to before global declaration" +#define GLOBAL_AFTER_USE \ +"name '%.400s' is used prior to global declaration" -PyObject * -PySymtableEntry_New(struct symtable *st, char *name, int type, int lineno) +PySTEntryObject * +PySTEntry_New(struct symtable *st, identifier name, block_ty block, + void *key, int lineno) { - PySymtableEntryObject *ste = NULL; + PySTEntryObject *ste = NULL; PyObject *k, *v; - k = PyInt_FromLong(st->st_nscopes++); + k = PyLong_FromVoidPtr(key); if (k == NULL) goto fail; - v = PyDict_GetItem(st->st_symbols, k); - if (v) { - Py_DECREF(k); - Py_INCREF(v); - return v; - } - - ste = (PySymtableEntryObject *)PyObject_New(PySymtableEntryObject, - &PySymtableEntry_Type); + ste = (PySTEntryObject *)PyObject_New(PySTEntryObject, + &PySTEntry_Type); ste->ste_table = st; ste->ste_id = k; + ste->ste_tmpname = 0; - v = PyString_FromString(name); - if (v == NULL) - goto fail; - ste->ste_name = v; + ste->ste_name = name; + Py_INCREF(name); v = PyDict_New(); if (v == NULL) @@ -59,61 +46,46 @@ PySymtableEntry_New(struct symtable *st, char *name, int type, int lineno) goto fail; ste->ste_children = v; - ste->ste_optimized = 0; + ste->ste_type = block; + ste->ste_unoptimized = 0; + ste->ste_nested = 0; + ste->ste_free = 0; + ste->ste_varargs = 0; + ste->ste_varkeywords = 0; ste->ste_opt_lineno = 0; ste->ste_tmpname = 0; ste->ste_lineno = lineno; - switch (type) { - case funcdef: - case lambdef: - case testlist_gexp: /* generator expression */ - case argument: /* generator expression */ - ste->ste_type = TYPE_FUNCTION; - break; - case classdef: - ste->ste_type = TYPE_CLASS; - break; - case single_input: - case eval_input: - case file_input: - ste->ste_type = TYPE_MODULE; - break; - } - if (st->st_cur == NULL) - ste->ste_nested = 0; - else if (st->st_cur->ste_nested - || st->st_cur->ste_type == TYPE_FUNCTION) + if (st->st_cur != NULL && + (st->st_cur->ste_nested || + st->st_cur->ste_type == FunctionBlock)) ste->ste_nested = 1; - else - ste->ste_nested = 0; ste->ste_child_free = 0; ste->ste_generator = 0; if (PyDict_SetItem(st->st_symbols, ste->ste_id, (PyObject *)ste) < 0) goto fail; - return (PyObject *)ste; + return ste; fail: Py_XDECREF(ste); return NULL; } static PyObject * -ste_repr(PySymtableEntryObject *ste) +ste_repr(PySTEntryObject *ste) { char buf[256]; PyOS_snprintf(buf, sizeof(buf), "", PyString_AS_STRING(ste->ste_name), - PyInt_AS_LONG(ste->ste_id), - ste->ste_lineno); + PyInt_AS_LONG(ste->ste_id), ste->ste_lineno); return PyString_FromString(buf); } static void -ste_dealloc(PySymtableEntryObject *ste) +ste_dealloc(PySTEntryObject *ste) { ste->ste_table = NULL; Py_XDECREF(ste->ste_id); @@ -124,7 +96,7 @@ ste_dealloc(PySymtableEntryObject *ste) PyObject_Del(ste); } -#define OFF(x) offsetof(PySymtableEntryObject, x) +#define OFF(x) offsetof(PySTEntryObject, x) static PyMemberDef ste_memberlist[] = { {"id", T_OBJECT, OFF(ste_id), READONLY}, @@ -134,16 +106,14 @@ static PyMemberDef ste_memberlist[] = { {"children", T_OBJECT, OFF(ste_children), READONLY}, {"type", T_INT, OFF(ste_type), READONLY}, {"lineno", T_INT, OFF(ste_lineno), READONLY}, - {"optimized",T_INT, OFF(ste_optimized), READONLY}, - {"nested", T_INT, OFF(ste_nested), READONLY}, {NULL} }; -PyTypeObject PySymtableEntry_Type = { +PyTypeObject PySTEntry_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "symtable entry", - sizeof(PySymtableEntryObject), + sizeof(PySTEntryObject), 0, (destructor)ste_dealloc, /* tp_dealloc */ 0, /* tp_print */ @@ -180,3 +150,1148 @@ PyTypeObject PySymtableEntry_Type = { 0, /* tp_alloc */ 0, /* tp_new */ }; + +static int symtable_analyze(struct symtable *st); +static int symtable_warn(struct symtable *st, char *msg); +static int symtable_enter_block(struct symtable *st, identifier name, + block_ty block, void *ast, int lineno); +static int symtable_exit_block(struct symtable *st, void *ast); +static int symtable_visit_stmt(struct symtable *st, stmt_ty s); +static int symtable_visit_expr(struct symtable *st, expr_ty s); +static int symtable_visit_genexp(struct symtable *st, expr_ty s); +static int symtable_visit_arguments(struct symtable *st, arguments_ty); +static int symtable_visit_excepthandler(struct symtable *st, excepthandler_ty); +static int symtable_visit_alias(struct symtable *st, alias_ty); +static int symtable_visit_comprehension(struct symtable *st, comprehension_ty); +static int symtable_visit_keyword(struct symtable *st, keyword_ty); +static int symtable_visit_slice(struct symtable *st, slice_ty); +static int symtable_visit_params(struct symtable *st, asdl_seq *args, int top); +static int symtable_visit_params_nested(struct symtable *st, asdl_seq *args); +static int symtable_implicit_arg(struct symtable *st, int pos); + + +static identifier top = NULL, lambda = NULL; + +#define GET_IDENTIFIER(VAR) \ + ((VAR) ? (VAR) : ((VAR) = PyString_InternFromString(# VAR))) + +#define DUPLICATE_ARGUMENT \ +"duplicate argument '%s' in function definition" + +static struct symtable * +symtable_new(void) +{ + struct symtable *st; + + st = (struct symtable *)PyMem_Malloc(sizeof(struct symtable)); + if (st == NULL) + return NULL; + + st->st_filename = NULL; + if ((st->st_stack = PyList_New(0)) == NULL) + goto fail; + if ((st->st_symbols = PyDict_New()) == NULL) + goto fail; + st->st_cur = NULL; + st->st_tmpname = 0; + st->st_private = NULL; + return st; + fail: + PySymtable_Free(st); + return NULL; +} + +struct symtable * +PySymtable_Build(mod_ty mod, const char *filename, PyFutureFeatures *future) +{ + struct symtable *st = symtable_new(); + asdl_seq *seq; + int i; + + if (st == NULL) + return st; + st->st_filename = filename; + st->st_future = future; + symtable_enter_block(st, GET_IDENTIFIER(top), ModuleBlock, + (void *)mod, 0); + st->st_top = st->st_cur; + st->st_cur->ste_unoptimized = OPT_TOPLEVEL; + /* Any other top-level initialization? */ + switch (mod->kind) { + case Module_kind: + seq = mod->v.Module.body; + for (i = 0; i < asdl_seq_LEN(seq); i++) + if (!symtable_visit_stmt(st, asdl_seq_GET(seq, i))) + goto error; + break; + case Expression_kind: + if (!symtable_visit_expr(st, mod->v.Expression.body)) + goto error; + break; + case Interactive_kind: + seq = mod->v.Interactive.body; + for (i = 0; i < asdl_seq_LEN(seq); i++) + if (!symtable_visit_stmt(st, asdl_seq_GET(seq, i))) + goto error; + break; + case Suite_kind: + PyErr_SetString(PyExc_RuntimeError, + "this compiler does not handle Suites"); + return NULL; + } + if (!symtable_exit_block(st, (void *)mod)) + return NULL; + if (symtable_analyze(st)) + return st; + error: + PySymtable_Free(st); + return NULL; +} + +void +PySymtable_Free(struct symtable *st) +{ + Py_XDECREF(st->st_symbols); + Py_XDECREF(st->st_stack); + PyMem_Free((void *)st); +} + +PySTEntryObject * +PySymtable_Lookup(struct symtable *st, void *key) +{ + PyObject *k, *v; + + k = PyLong_FromVoidPtr(key); + if (k == NULL) + return NULL; + v = PyDict_GetItem(st->st_symbols, k); + if (v) { + assert(PySTEntry_Check(v)); + Py_DECREF(k); + Py_INCREF(v); + return (PySTEntryObject *)v; + } + else { + PyErr_SetString(PyExc_KeyError, + "unknown symbol table entry"); + return NULL; + } +} + +int +PyST_GetScope(PySTEntryObject *ste, PyObject *name) +{ + PyObject *v = PyDict_GetItem(ste->ste_symbols, name); + if (!v) + return 0; + assert(PyInt_Check(v)); + return (PyInt_AS_LONG(v) >> SCOPE_OFF) & SCOPE_MASK; +} + + +/* Analyze raw symbol information to determine scope of each name. + + The next several functions are helpers for PySymtable_Analyze(), + which determines whether a name is local, global, or free. In addition, + it determines which local variables are cell variables; they provide + bindings that are used for free variables in enclosed blocks. + + There are also two kinds of free variables, implicit and explicit. An + explicit global is declared with the global statement. An implicit + global is a free variable for which the compiler has found no binding + in an enclosing function scope. The implicit global is either a global + or a builtin. Python's module and class blocks use the xxx_NAME opcodes + to handle these names to implement slightly odd semantics. In such a + block, the name is treated as global until it is assigned to; then it + is treated as a local. + + The symbol table requires two passes to determine the scope of each name. + The first pass collects raw facts from the AST: the name is a parameter + here, the name is used by not defined here, etc. The second pass analyzes + these facts during a pass over the PySTEntryObjects created during pass 1. + + When a function is entered during the second pass, the parent passes + the set of all name bindings visible to its children. These bindings + are used to determine if the variable is free or an implicit global. + After doing the local analysis, it analyzes each of its child blocks + using an updated set of name bindings. + + The children update the free variable set. If a local variable is free + in a child, the variable is marked as a cell. The current function must + provide runtime storage for the variable that may outlive the function's + frame. Cell variables are removed from the free set before the analyze + function returns to its parent. + + The sets of bound and free variables are implemented as dictionaries + mapping strings to None. +*/ + +#define SET_SCOPE(DICT, NAME, I) { \ + PyObject *o = PyInt_FromLong(I); \ + if (!o) \ + return 0; \ + if (PyDict_SetItem((DICT), (NAME), o) < 0) \ + return 0; \ +} + +/* Decide on scope of name, given flags. + + The dicts passed in as arguments are modified as necessary. + ste is passed so that flags can be updated. +*/ + +static int +analyze_name(PySTEntryObject *ste, PyObject *dict, PyObject *name, int flags, + PyObject *bound, PyObject *local, PyObject *free, + PyObject *global) +{ + if (flags & DEF_GLOBAL) { + if (flags & DEF_PARAM) { + PyErr_Format(PyExc_SyntaxError, + "name '%s' is local and global", + PyString_AS_STRING(name)); + return 0; + } + SET_SCOPE(dict, name, GLOBAL_EXPLICIT); + if (PyDict_SetItem(global, name, Py_None) < 0) + return 0; + if (bound && PyDict_GetItem(bound, name)) { + if (PyDict_DelItem(bound, name) < 0) + return 0; + } + return 1; + } + if (flags & DEF_BOUND) { + SET_SCOPE(dict, name, LOCAL); + if (PyDict_SetItem(local, name, Py_None) < 0) + return 0; + if (PyDict_GetItem(global, name)) { + if (PyDict_DelItem(global, name) < 0) + return 0; + } + return 1; + } + /* If an enclosing block has a binding for this name, it + is a free variable rather than a global variable. + Note that having a non-NULL bound implies that the block + is nested. + */ + if (bound && PyDict_GetItem(bound, name)) { + SET_SCOPE(dict, name, FREE); + ste->ste_free = 1; + if (PyDict_SetItem(free, name, Py_None) < 0) + return 0; + return 1; + } + /* If a parent has a global statement, then call it global + explicit? It could also be global implicit. + */ + else if (global && PyDict_GetItem(global, name)) { + SET_SCOPE(dict, name, GLOBAL_EXPLICIT); + return 1; + } + else { + if (ste->ste_nested) + ste->ste_free = 1; + SET_SCOPE(dict, name, GLOBAL_IMPLICIT); + return 1; + } + return 0; /* Can't get here */ +} + +#undef SET_SCOPE + +/* If a name is defined in free and also in locals, then this block + provides the binding for the free variable. The name should be + marked CELL in this block and removed from the free list. + + Note that the current block's free variables are included in free. + That's safe because no name can be free and local in the same scope. +*/ + +static int +analyze_cells(PyObject *scope, PyObject *free) +{ + PyObject *name, *v, *w; + int flags, pos = 0, success = 0; + + w = PyInt_FromLong(CELL); + if (!w) + return 0; + while (PyDict_Next(scope, &pos, &name, &v)) { + assert(PyInt_Check(v)); + flags = PyInt_AS_LONG(v); + if (flags != LOCAL) + continue; + if (!PyDict_GetItem(free, name)) + continue; + /* Replace LOCAL with CELL for this name, and remove + from free. It is safe to replace the value of name + in the dict, because it will not cause a resize. + */ + if (PyDict_SetItem(scope, name, w) < 0) + goto error; + if (!PyDict_DelItem(free, name) < 0) + goto error; + } + success = 1; + error: + Py_DECREF(w); + return success; +} + +/* Check for illegal statements in unoptimized namespaces */ +static int +check_unoptimized(const PySTEntryObject* ste) { + char buf[300]; + + if (ste->ste_type == ModuleBlock || !ste->ste_unoptimized + || !(ste->ste_free || ste->ste_child_free)) + return 1; + + const char* trailer = (ste->ste_child_free ? + "contains a nested function with free variables" : + "is a nested function"); + + switch (ste->ste_unoptimized) { + case OPT_TOPLEVEL: /* exec / import * at top-level is fine */ + case OPT_EXEC: /* qualified exec is fine */ + return 1; + case OPT_IMPORT_STAR: + PyOS_snprintf(buf, sizeof(buf), + "import * is not allowed in function '%.100s' " + "because it is %s", + PyString_AS_STRING(ste->ste_name), trailer); + break; + case OPT_BARE_EXEC: + PyOS_snprintf(buf, sizeof(buf), + "unqualified exec is not allowed in function " + "'%.100s' it %s", + PyString_AS_STRING(ste->ste_name), trailer); + break; + default: + PyOS_snprintf(buf, sizeof(buf), + "function '%.100s' uses import * and bare exec, " + "which are illegal because it %s", + PyString_AS_STRING(ste->ste_name), trailer); + break; + } + + PyErr_SetString(PyExc_SyntaxError, buf); + PyErr_SyntaxLocation(ste->ste_table->st_filename, + ste->ste_opt_lineno); + return 0; +} + +/* Enter the final scope information into the st_symbols dict. + * + * All arguments are dicts. Modifies symbols, others are read-only. +*/ +static int +update_symbols(PyObject *symbols, PyObject *scope, + PyObject *bound, PyObject *free, int class) +{ + PyObject *name, *v, *u, *w, *free_value = NULL; + int i, flags, pos = 0; + + while (PyDict_Next(symbols, &pos, &name, &v)) { + assert(PyInt_Check(v)); + flags = PyInt_AS_LONG(v); + w = PyDict_GetItem(scope, name); + assert(w && PyInt_Check(w)); + i = PyInt_AS_LONG(w); + flags |= (i << SCOPE_OFF); + u = PyInt_FromLong(flags); + if (PyDict_SetItem(symbols, name, u) < 0) { + Py_DECREF(u); + return 0; + } + Py_DECREF(u); + } + + free_value = PyInt_FromLong(FREE << SCOPE_OFF); + if (!free_value) + return 0; + + /* add a free variable when it's only use is for creating a closure */ + pos = 0; + while (PyDict_Next(free, &pos, &name, &v)) { + PyObject *o = PyDict_GetItem(symbols, name); + + if (o) { + /* It could be a free variable in a method of + the class that has the same name as a local + or global in the class scope. + */ + if (class && + PyInt_AS_LONG(o) & (DEF_BOUND | DEF_GLOBAL)) { + int i = PyInt_AS_LONG(o) | DEF_FREE_CLASS; + o = PyInt_FromLong(i); + if (!o) { + Py_DECREF(free_value); + return 0; + } + if (PyDict_SetItem(symbols, name, o) < 0) { + Py_DECREF(o); + Py_DECREF(free_value); + return 0; + } + } + /* else it's not free, probably a cell */ + continue; + } + if (!PyDict_GetItem(bound, name)) + continue; /* it's a global */ + + if (PyDict_SetItem(symbols, name, free_value) < 0) { + Py_DECREF(free_value); + return 0; + } + } + Py_DECREF(free_value); + return 1; +} + +/* Make final symbol table decisions for block of ste. + Arguments: + ste -- current symtable entry (input/output) + bound -- set of variables bound in enclosing scopes (input) + free -- set of free variables in enclosed scopes (output) + globals -- set of declared global variables in enclosing scopes (input) +*/ + +static int +analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, + PyObject *global) +{ + PyObject *name, *v, *local = NULL, *scope = NULL, *newbound = NULL; + PyObject *newglobal = NULL, *newfree = NULL; + int i, flags, pos = 0, success = 0; + + local = PyDict_New(); + if (!local) + goto error; + scope = PyDict_New(); + if (!scope) + goto error; + newglobal = PyDict_New(); + if (!newglobal) + goto error; + newfree = PyDict_New(); + if (!newfree) + goto error; + newbound = PyDict_New(); + if (!newbound) + goto error; + + if (ste->ste_type == ClassBlock) { + /* make a copy of globals before calling analyze_name(), + because global statements in the class have no effect + on nested functions. + */ + if (PyDict_Update(newglobal, global) < 0) + goto error; + if (bound) + if (PyDict_Update(newbound, bound) < 0) + goto error; + } + + assert(PySTEntry_Check(ste)); + assert(PyDict_Check(ste->ste_symbols)); + while (PyDict_Next(ste->ste_symbols, &pos, &name, &v)) { + flags = PyInt_AS_LONG(v); + if (!analyze_name(ste, scope, name, flags, bound, local, free, + global)) + goto error; + } + + if (ste->ste_type != ClassBlock) { + if (ste->ste_type == FunctionBlock) { + if (PyDict_Update(newbound, local) < 0) + goto error; + } + if (bound) { + if (PyDict_Update(newbound, bound) < 0) + goto error; + } + if (PyDict_Update(newglobal, global) < 0) + goto error; + } + + /* Recursively call analyze_block() on each child block */ + for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) { + PyObject *c = PyList_GET_ITEM(ste->ste_children, i); + assert(c && PySTEntry_Check(c)); + PySTEntryObject* entry = (PySTEntryObject*)c; + if (!analyze_block(entry, newbound, newfree, newglobal)) + goto error; + if (entry->ste_free || entry->ste_child_free) + ste->ste_child_free = 1; + } + + if (ste->ste_type == FunctionBlock && !analyze_cells(scope, newfree)) + goto error; + if (!update_symbols(ste->ste_symbols, scope, bound, newfree, + ste->ste_type == ClassBlock)) + goto error; + if (!check_unoptimized(ste)) + goto error; + + if (PyDict_Update(free, newfree) < 0) + goto error; + success = 1; + error: + Py_XDECREF(local); + Py_XDECREF(scope); + Py_XDECREF(newbound); + Py_XDECREF(newglobal); + Py_XDECREF(newfree); + if (!success) + assert(PyErr_Occurred()); + return success; +} + +static int +symtable_analyze(struct symtable *st) +{ + PyObject *free, *global; + int r; + + free = PyDict_New(); + if (!free) + return 0; + global = PyDict_New(); + if (!global) { + Py_DECREF(global); + return 0; + } + r = analyze_block(st->st_top, NULL, free, global); + Py_DECREF(free); + Py_DECREF(global); + return r; +} + + +static int +symtable_warn(struct symtable *st, char *msg) +{ + if (PyErr_WarnExplicit(PyExc_SyntaxWarning, msg, st->st_filename, + st->st_cur->ste_lineno, NULL, NULL) < 0) { + if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) { + PyErr_SetString(PyExc_SyntaxError, msg); + PyErr_SyntaxLocation(st->st_filename, + st->st_cur->ste_lineno); + } + return 0; + } + return 1; +} + +/* symtable_enter_block() gets a reference via PySTEntry_New(). + This reference is released when the block is exited, via the DECREF + in symtable_exit_block(). +*/ + +static int +symtable_exit_block(struct symtable *st, void *ast) +{ + int end; + + Py_DECREF(st->st_cur); + end = PyList_GET_SIZE(st->st_stack) - 1; + if (end >= 0) { + st->st_cur = (PySTEntryObject *)PyList_GET_ITEM(st->st_stack, + end); + Py_INCREF(st->st_cur); + if (PySequence_DelItem(st->st_stack, end) < 0) + return 0; + } + return 1; +} + +static int +symtable_enter_block(struct symtable *st, identifier name, block_ty block, + void *ast, int lineno) +{ + PySTEntryObject *prev = NULL; + + if (st->st_cur) { + prev = st->st_cur; + if (PyList_Append(st->st_stack, (PyObject *)st->st_cur) < 0) { + Py_DECREF(st->st_cur); + return 0; + } + Py_DECREF(st->st_cur); + } + st->st_cur = PySTEntry_New(st, name, block, ast, lineno); + if (name == GET_IDENTIFIER(top)) + st->st_global = st->st_cur->ste_symbols; + if (prev) { + if (PyList_Append(prev->ste_children, + (PyObject *)st->st_cur) < 0) { + return 0; + } + } + return 1; +} + +static int +symtable_lookup(struct symtable *st, PyObject *name) +{ + PyObject *o; + + o = PyDict_GetItem(st->st_cur->ste_symbols, name); + if (!o) + return 0; + return PyInt_AsLong(o); +} + +static int +symtable_add_def(struct symtable *st, PyObject *name, int flag) +{ + PyObject *o; + PyObject *dict; + int val; + + dict = st->st_cur->ste_symbols; + if ((o = PyDict_GetItem(dict, name))) { + val = PyInt_AS_LONG(o); + if ((flag & DEF_PARAM) && (val & DEF_PARAM)) { + PyErr_Format(PyExc_SyntaxError, DUPLICATE_ARGUMENT, + PyString_AsString(name)); + PyErr_SyntaxLocation(st->st_filename, + st->st_cur->ste_lineno); + return 0; + } + val |= flag; + } else + val = flag; + o = PyInt_FromLong(val); + if (o == NULL) + return 0; + if (PyDict_SetItem(dict, name, o) < 0) { + Py_DECREF(o); + return 0; + } + Py_DECREF(o); + + if (flag & DEF_PARAM) { + if (PyList_Append(st->st_cur->ste_varnames, name) < 0) + return 0; + } else if (flag & DEF_GLOBAL) { + /* XXX need to update DEF_GLOBAL for other flags too; + perhaps only DEF_FREE_GLOBAL */ + val = flag; + if ((o = PyDict_GetItem(st->st_global, name))) { + val |= PyInt_AS_LONG(o); + } + o = PyInt_FromLong(val); + if (o == NULL) + return 0; + if (PyDict_SetItem(st->st_global, name, o) < 0) { + Py_DECREF(o); + return 0; + } + Py_DECREF(o); + } + return 1; +} + +/* VISIT, VISIT_SEQ and VIST_SEQ_TAIL take an ASDL type as their second argument. + They use the ASDL name to synthesize the name of the C type and the visit + function. + + VISIT_SEQ_TAIL permits the start of an ASDL sequence to be skipped, which is + useful if the first node in the sequence requires special treatment. +*/ + +#define VISIT(ST, TYPE, V) \ + if (!symtable_visit_ ## TYPE((ST), (V))) \ + return 0; + +#define VISIT_SEQ(ST, TYPE, SEQ) { \ + int i; \ + asdl_seq *seq = (SEQ); /* avoid variable capture */ \ + for (i = 0; i < asdl_seq_LEN(seq); i++) { \ + TYPE ## _ty elt = asdl_seq_GET(seq, i); \ + if (!symtable_visit_ ## TYPE((ST), elt)) \ + return 0; \ + } \ +} + +#define VISIT_SEQ_TAIL(ST, TYPE, SEQ, START) { \ + int i; \ + asdl_seq *seq = (SEQ); /* avoid variable capture */ \ + for (i = (START); i < asdl_seq_LEN(seq); i++) { \ + TYPE ## _ty elt = asdl_seq_GET(seq, i); \ + if (!symtable_visit_ ## TYPE((ST), elt)) \ + return 0; \ + } \ +} + +static int +symtable_visit_stmt(struct symtable *st, stmt_ty s) +{ + switch (s->kind) { + case FunctionDef_kind: + if (!symtable_add_def(st, s->v.FunctionDef.name, DEF_LOCAL)) + return 0; + if (s->v.FunctionDef.args->defaults) + VISIT_SEQ(st, expr, s->v.FunctionDef.args->defaults); + if (s->v.FunctionDef.decorators) + VISIT_SEQ(st, expr, s->v.FunctionDef.decorators); + if (!symtable_enter_block(st, s->v.FunctionDef.name, + FunctionBlock, (void *)s, s->lineno)) + return 0; + VISIT(st, arguments, s->v.FunctionDef.args); + VISIT_SEQ(st, stmt, s->v.FunctionDef.body); + if (!symtable_exit_block(st, s)) + return 0; + break; + case ClassDef_kind: + if (!symtable_add_def(st, s->v.ClassDef.name, DEF_LOCAL)) + return 0; + VISIT_SEQ(st, expr, s->v.ClassDef.bases); + if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock, + (void *)s, s->lineno)) + return 0; + VISIT_SEQ(st, stmt, s->v.ClassDef.body); + if (!symtable_exit_block(st, s)) + return 0; + break; + case Return_kind: + if (s->v.Return.value) + VISIT(st, expr, s->v.Return.value); + break; + case Delete_kind: + VISIT_SEQ(st, expr, s->v.Delete.targets); + break; + case Assign_kind: + VISIT_SEQ(st, expr, s->v.Assign.targets); + VISIT(st, expr, s->v.Assign.value); + break; + case AugAssign_kind: + VISIT(st, expr, s->v.AugAssign.target); + VISIT(st, expr, s->v.AugAssign.value); + break; + case Print_kind: + if (s->v.Print.dest) + VISIT(st, expr, s->v.Print.dest); + VISIT_SEQ(st, expr, s->v.Print.values); + break; + case For_kind: + VISIT(st, expr, s->v.For.target); + VISIT(st, expr, s->v.For.iter); + VISIT_SEQ(st, stmt, s->v.For.body); + if (s->v.For.orelse) + VISIT_SEQ(st, stmt, s->v.For.orelse); + break; + case While_kind: + VISIT(st, expr, s->v.While.test); + VISIT_SEQ(st, stmt, s->v.While.body); + if (s->v.While.orelse) + VISIT_SEQ(st, stmt, s->v.While.orelse); + break; + case If_kind: + /* XXX if 0: and lookup_yield() hacks */ + VISIT(st, expr, s->v.If.test); + VISIT_SEQ(st, stmt, s->v.If.body); + if (s->v.If.orelse) + VISIT_SEQ(st, stmt, s->v.If.orelse); + break; + case Raise_kind: + if (s->v.Raise.type) { + VISIT(st, expr, s->v.Raise.type); + if (s->v.Raise.inst) { + VISIT(st, expr, s->v.Raise.inst); + if (s->v.Raise.tback) + VISIT(st, expr, s->v.Raise.tback); + } + } + break; + case TryExcept_kind: + VISIT_SEQ(st, stmt, s->v.TryExcept.body); + VISIT_SEQ(st, stmt, s->v.TryExcept.orelse); + VISIT_SEQ(st, excepthandler, s->v.TryExcept.handlers); + break; + case TryFinally_kind: + VISIT_SEQ(st, stmt, s->v.TryFinally.body); + VISIT_SEQ(st, stmt, s->v.TryFinally.finalbody); + break; + case Assert_kind: + VISIT(st, expr, s->v.Assert.test); + if (s->v.Assert.msg) + VISIT(st, expr, s->v.Assert.msg); + break; + case Import_kind: + VISIT_SEQ(st, alias, s->v.Import.names); + /* XXX Don't have the lineno available inside + visit_alias */ + if (st->st_cur->ste_unoptimized && !st->st_cur->ste_opt_lineno) + st->st_cur->ste_opt_lineno = s->lineno; + break; + case ImportFrom_kind: + VISIT_SEQ(st, alias, s->v.ImportFrom.names); + /* XXX Don't have the lineno available inside + visit_alias */ + if (st->st_cur->ste_unoptimized && !st->st_cur->ste_opt_lineno) + st->st_cur->ste_opt_lineno = s->lineno; + break; + case Exec_kind: + VISIT(st, expr, s->v.Exec.body); + if (!st->st_cur->ste_opt_lineno) + st->st_cur->ste_opt_lineno = s->lineno; + if (s->v.Exec.globals) { + st->st_cur->ste_unoptimized |= OPT_EXEC; + VISIT(st, expr, s->v.Exec.globals); + if (s->v.Exec.locals) + VISIT(st, expr, s->v.Exec.locals); + } else { + st->st_cur->ste_unoptimized |= OPT_BARE_EXEC; + } + break; + case Global_kind: { + int i; + asdl_seq *seq = s->v.Global.names; + for (i = 0; i < asdl_seq_LEN(seq); i++) { + identifier name = asdl_seq_GET(seq, i); + char *c_name = PyString_AS_STRING(name); + int cur = symtable_lookup(st, name); + if (cur < 0) + return 0; + if (cur & (DEF_LOCAL | USE)) { + char buf[1000]; + if (cur & DEF_LOCAL) + PyOS_snprintf(buf, sizeof(buf), + GLOBAL_AFTER_ASSIGN, + c_name); + else + PyOS_snprintf(buf, sizeof(buf), + GLOBAL_AFTER_USE, + c_name); + if (!symtable_warn(st, buf)) + return 0; + } + if (!symtable_add_def(st, name, DEF_GLOBAL)) + return 0; + + } + + break; + } + case Expr_kind: + VISIT(st, expr, s->v.Expr.value); + break; + case Pass_kind: + case Break_kind: + case Continue_kind: + /* nothing to do here */ + break; + } + return 1; +} + +static int +symtable_visit_expr(struct symtable *st, expr_ty e) +{ + switch (e->kind) { + case BoolOp_kind: + VISIT_SEQ(st, expr, e->v.BoolOp.values); + break; + case BinOp_kind: + VISIT(st, expr, e->v.BinOp.left); + VISIT(st, expr, e->v.BinOp.right); + break; + case UnaryOp_kind: + VISIT(st, expr, e->v.UnaryOp.operand); + break; + case Lambda_kind: { + if (!symtable_add_def(st, GET_IDENTIFIER(lambda), DEF_LOCAL)) + return 0; + if (e->v.Lambda.args->defaults) + VISIT_SEQ(st, expr, e->v.Lambda.args->defaults); + /* XXX how to get line numbers for expressions */ + if (!symtable_enter_block(st, GET_IDENTIFIER(lambda), + FunctionBlock, (void *)e, 0)) + return 0; + VISIT(st, arguments, e->v.Lambda.args); + VISIT(st, expr, e->v.Lambda.body); + if (!symtable_exit_block(st, (void *)e)) + return 0; + break; + } + case Dict_kind: + VISIT_SEQ(st, expr, e->v.Dict.keys); + VISIT_SEQ(st, expr, e->v.Dict.values); + break; + case ListComp_kind: { + char tmpname[256]; + identifier tmp; + + PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", + ++st->st_cur->ste_tmpname); + tmp = PyString_FromString(tmpname); + if (!symtable_add_def(st, tmp, DEF_LOCAL)) + return 0; + VISIT(st, expr, e->v.ListComp.elt); + VISIT_SEQ(st, comprehension, e->v.ListComp.generators); + break; + } + case GeneratorExp_kind: { + if (!symtable_visit_genexp(st, e)) { + return 0; + } + break; + } + case Yield_kind: + if (e->v.Yield.value) + VISIT(st, expr, e->v.Yield.value); + st->st_cur->ste_generator = 1; + break; + case Compare_kind: + VISIT(st, expr, e->v.Compare.left); + VISIT_SEQ(st, expr, e->v.Compare.comparators); + break; + case Call_kind: + VISIT(st, expr, e->v.Call.func); + VISIT_SEQ(st, expr, e->v.Call.args); + VISIT_SEQ(st, keyword, e->v.Call.keywords); + if (e->v.Call.starargs) + VISIT(st, expr, e->v.Call.starargs); + if (e->v.Call.kwargs) + VISIT(st, expr, e->v.Call.kwargs); + break; + case Repr_kind: + VISIT(st, expr, e->v.Repr.value); + break; + case Num_kind: + case Str_kind: + /* Nothing to do here. */ + break; + /* The following exprs can be assignment targets. */ + case Attribute_kind: + VISIT(st, expr, e->v.Attribute.value); + break; + case Subscript_kind: + VISIT(st, expr, e->v.Subscript.value); + VISIT(st, slice, e->v.Subscript.slice); + break; + case Name_kind: + if (!symtable_add_def(st, e->v.Name.id, + e->v.Name.ctx == Load ? USE : DEF_LOCAL)) + return 0; + break; + /* child nodes of List and Tuple will have expr_context set */ + case List_kind: + VISIT_SEQ(st, expr, e->v.List.elts); + break; + case Tuple_kind: + VISIT_SEQ(st, expr, e->v.Tuple.elts); + break; + } + return 1; +} + +static int +symtable_implicit_arg(struct symtable *st, int pos) +{ + PyObject *id = PyString_FromFormat(".%d", pos); + if (id == NULL) + return 0; + if (!symtable_add_def(st, id, DEF_PARAM)) { + Py_DECREF(id); + return 0; + } + Py_DECREF(id); + return 1; +} + +static int +symtable_visit_params(struct symtable *st, asdl_seq *args, int toplevel) +{ + int i, complex = 0; + + /* go through all the toplevel arguments first */ + for (i = 0; i < asdl_seq_LEN(args); i++) { + expr_ty arg = asdl_seq_GET(args, i); + if (arg->kind == Name_kind) { + assert(arg->v.Name.ctx == Param || + (arg->v.Name.ctx == Store && !toplevel)); + if (!symtable_add_def(st, arg->v.Name.id, DEF_PARAM)) + return 0; + } + else if (arg->kind == Tuple_kind) { + assert(arg->v.Tuple.ctx == Store); + complex = 1; + if (toplevel) { + if (!symtable_implicit_arg(st, i)) + return 0; + } + } + else { + /* syntax error */ + fprintf(stderr, "unexpected expr in parameter list\n"); + return 0; + } + } + + if (!toplevel) { + if (!symtable_visit_params_nested(st, args)) + return 0; + } + + return 1; +} + +static int +symtable_visit_params_nested(struct symtable *st, asdl_seq *args) +{ + int i; + for (i = 0; i < asdl_seq_LEN(args); i++) { + expr_ty arg = asdl_seq_GET(args, i); + if (arg->kind == Tuple_kind && + !symtable_visit_params(st, arg->v.Tuple.elts, 0)) + return 0; + } + + return 1; +} + +static int +symtable_visit_arguments(struct symtable *st, arguments_ty a) +{ + /* skip default arguments inside function block + XXX should ast be different? + */ + if (a->args && !symtable_visit_params(st, a->args, 1)) + return 0; + if (a->vararg) { + if (!symtable_add_def(st, a->vararg, DEF_PARAM)) + return 0; + st->st_cur->ste_varargs = 1; + } + if (a->kwarg) { + if (!symtable_add_def(st, a->kwarg, DEF_PARAM)) + return 0; + st->st_cur->ste_varkeywords = 1; + } + if (a->args && !symtable_visit_params_nested(st, a->args)) + return 0; + return 1; +} + + +static int +symtable_visit_excepthandler(struct symtable *st, excepthandler_ty eh) +{ + if (eh->type) + VISIT(st, expr, eh->type); + if (eh->name) + VISIT(st, expr, eh->name); + VISIT_SEQ(st, stmt, eh->body); + return 1; +} + + +static int +symtable_visit_alias(struct symtable *st, alias_ty a) +{ + /* Compute store_name, the name actually bound by the import + operation. It is diferent than a->name when a->name is a + dotted package name (e.g. spam.eggs) + */ + PyObject *store_name; + PyObject *name = (a->asname == NULL) ? a->name : a->asname; + const char *base = PyString_AS_STRING(name); + char *dot = strchr(base, '.'); + if (dot) + store_name = PyString_FromStringAndSize(base, dot - base); + else { + store_name = name; + Py_INCREF(store_name); + } + if (strcmp(PyString_AS_STRING(name), "*")) { + int r = symtable_add_def(st, store_name, DEF_IMPORT); + Py_DECREF(store_name); + return r; + } + else { + if (st->st_cur->ste_type != ModuleBlock) { + if (!symtable_warn(st, + "import * only allowed at module level")) + return 0; + } + st->st_cur->ste_unoptimized |= OPT_IMPORT_STAR; + return 1; + } +} + + +static int +symtable_visit_comprehension(struct symtable *st, comprehension_ty lc) +{ + VISIT(st, expr, lc->target); + VISIT(st, expr, lc->iter); + VISIT_SEQ(st, expr, lc->ifs); + return 1; +} + + +static int +symtable_visit_keyword(struct symtable *st, keyword_ty k) +{ + VISIT(st, expr, k->value); + return 1; +} + + +static int +symtable_visit_slice(struct symtable *st, slice_ty s) +{ + switch (s->kind) { + case Slice_kind: + if (s->v.Slice.lower) + VISIT(st, expr, s->v.Slice.lower) + if (s->v.Slice.upper) + VISIT(st, expr, s->v.Slice.upper) + if (s->v.Slice.step) + VISIT(st, expr, s->v.Slice.step) + break; + case ExtSlice_kind: + VISIT_SEQ(st, slice, s->v.ExtSlice.dims) + break; + case Index_kind: + VISIT(st, expr, s->v.Index.value) + break; + case Ellipsis_kind: + break; + } + return 1; +} + +static int +symtable_visit_genexp(struct symtable *st, expr_ty e) +{ + identifier tmp; + comprehension_ty outermost = ((comprehension_ty) + (asdl_seq_GET(e->v.GeneratorExp.generators, 0))); + /* Outermost iterator is evaluated in current scope */ + VISIT(st, expr, outermost->iter); + /* Create generator scope for the rest */ + tmp = PyString_FromString(""); + if (!symtable_enter_block(st, tmp, FunctionBlock, (void *)e, 0)) { + return 0; + } + st->st_cur->ste_generator = 1; + /* Outermost iter is received as an argument */ + if (!symtable_implicit_arg(st, 0)) { + return 0; + } + VISIT(st, expr, outermost->target); + VISIT_SEQ(st, expr, outermost->ifs); + VISIT_SEQ_TAIL(st, comprehension, e->v.GeneratorExp.generators, 1); + VISIT(st, expr, e->v.GeneratorExp.elt); + if (!symtable_exit_block(st, (void *)e)) + return 0; + return 1; +} diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 0775bb8..d9f1337 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -15,7 +15,7 @@ Data members: */ #include "Python.h" -#include "compile.h" +#include "code.h" #include "frameobject.h" #include "eval.h" diff --git a/Python/traceback.c b/Python/traceback.c index f40cfb4..b58e8ad 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -3,7 +3,7 @@ #include "Python.h" -#include "compile.h" +#include "code.h" #include "frameobject.h" #include "structmember.h" #include "osdefs.h" diff --git a/Tools/compiler/dumppyc.py b/Tools/compiler/dumppyc.py index dd460c9..8cfe3b1 100755 --- a/Tools/compiler/dumppyc.py +++ b/Tools/compiler/dumppyc.py @@ -28,7 +28,7 @@ def walk(co, match=None): if type(obj) == types.CodeType: walk(obj, match) -def main(filename, codename=None): +def load(filename, codename=None): co = loadCode(filename) walk(co, codename) @@ -39,6 +39,9 @@ if __name__ == "__main__": else: filename = sys.argv[1] codename = None - if filename.endswith('.py') and os.path.exists(filename+"c"): - filename += "c" - main(filename, codename) + if filename.endswith('.py'): + buf = open(filename).read() + co = compile(buf, filename, "exec") + walk(co) + else: + load(filename, codename) -- cgit v0.12