view Python/future.c @ 20491:149a65fa427c legacy-trunk

Fix computation of stack depth for classdef and closures. Also minor tweaks to internal routines. Use PyCF_MASK instead of explicit list of flags. For the MAKE_CLOSURE opcode, the number of items popped off the stack depends on both the oparg and the number of free variables for the code object. Fix the code so it accounts for the free variables. In com_classdef(), record an extra pop to account for the STORE call after the BUILD_CLASS. Get rid of some commented out debugging code in com_push() and com_pop(). Factor string resize logic into helper routine com_check_size(). In com_addbyte(), remove redudant if statement after assert. (They test the same condition.) In several routines, use string macros instead of string functions.
author Jeremy Hylton <jeremy@alum.mit.edu>
date Wed, 17 Oct 2001 13:22:22 +0000
parents 81c7565a56ac
children ba0236f84318
line wrap: on
line source

#include "Python.h"
#include "node.h"
#include "token.h"
#include "graminit.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, char *filename)
{
	int i;
	char *feature;
	node *ch;

	REQ(n, import_stmt); /* must by from __future__ import ... */

	for (i = 3; i < NCH(n); i += 2) {
		ch = CHILD(n, i);
		if (TYPE(ch) == STAR) {
			PyErr_SetString(PyExc_SyntaxError,
					FUTURE_IMPORT_STAR);
			PyErr_SyntaxLocation(filename, ch->n_lineno);
			return -1;
		}
		REQ(ch, import_as_name);
		feature = STR(CHILD(ch, 0));
		if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) {
			continue;
		} else if (strcmp(feature, FUTURE_GENERATORS) == 0) {
			ff->ff_features |= CO_GENERATOR_ALLOWED;
		} else if (strcmp(feature, FUTURE_DIVISION) == 0) {
			ff->ff_features |= CO_FUTURE_DIVISION;
		} else if (strcmp(feature, "braces") == 0) {
			PyErr_SetString(PyExc_SyntaxError,
					"not a chance");
			PyErr_SyntaxLocation(filename, CHILD(ch, 0)->n_lineno);
			return -1;
		} else {
			PyErr_Format(PyExc_SyntaxError,
				     UNDEFINED_FUTURE_FEATURE, feature);
			PyErr_SyntaxLocation(filename, CHILD(ch, 0)->n_lineno);
			return -1;
		}
	}
	return 0;
}

static void
future_error(node *n, 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
*/

static int
future_parse(PyFutureFeatures *ff, node *n, char *filename)
{
	int i, r;
 loop:

	switch (TYPE(n)) {

	case single_input:
		if (TYPE(CHILD(n, 0)) == simple_stmt) {
			n = CHILD(n, 0);
			goto loop;
		}
		return 0;

	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.
		*/
		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 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;

		if (STR(CHILD(n, 0))[0] != 'f') { /* from */
			ff->ff_last_lineno = n->n_lineno;
			return 0;
		}
		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;
		}
		break;

	case atom:
		if (TYPE(CHILD(n, 0)) == STRING 
		    && ff->ff_found_docstring == 0) {
			ff->ff_found_docstring = 1;
			return 0;
		}
		ff->ff_last_lineno = n->n_lineno;
		return 0;

	default:
		ff->ff_last_lineno = n->n_lineno;
		return 0;
	}
	return 0;
}

PyFutureFeatures *
PyNode_Future(node *n, 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;

	if (future_parse(ff, n, filename) < 0) {
		PyMem_Free((void *)ff);
		return NULL;
	}
	return ff;
}