mirror of
				https://github.com/python/cpython.git
				synced 2025-10-22 17:33:55 +00:00 
			
		
		
		
	 1e35ce58e8
			
		
	
	
		1e35ce58e8
		
	
	
	
	
		
			
			Check return value from future_parse() in for loop for file_input to accomodate multiple future statements on separate lines. Add several comments explaining how the code works. Remove out-dated XXX comment.
		
			
				
	
	
		
			260 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			260 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #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;
 | |
| }
 | |
| 
 |