Skip to content

Commit 39e2f3f

Browse files
committed
Presumed correct compiler pass for future statements
XXX still need to integrate into symtable API compile.h: Remove ff_n_simple_stmt; obsolete. Add ff_found_docstring used internally to skip one and only one string at the beginning of a module. compile.c: Add check for from __future__ imports to far into the file. In symtable_global() check for -1 returned from symtable_lookup(), which signifies name not defined. Add missing DECERF in symtable_add_def. Free c->c_future. future.c: Add special handling for multiple statements joined on a single line using one or more semicolons; this form can include an illegal future statement that would otherwise be hard to detect. Add support for detecting and skipping doc strings.
1 parent 8e43cd7 commit 39e2f3f

File tree

3 files changed

+129
-30
lines changed

3 files changed

+129
-30
lines changed

Include/compile.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ DL_IMPORT(int) PyCode_Addr2Line(PyCodeObject *, int);
5151
/* Future feature support */
5252

5353
typedef struct {
54+
int ff_found_docstring;
5455
int ff_last_lineno;
55-
int ff_n_simple_stmt;
5656
int ff_nested_scopes;
5757
} PyFutureFeatures;
5858

Python/compile.c

+30-4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ int Py_OptimizeFlag = 0;
6464
#define LOCAL_GLOBAL \
6565
"name '%.400s' is a function paramter and declared global"
6666

67+
#define LATE_FUTURE \
68+
"from __future__ imports must occur at the beginning of the file"
69+
6770
#define MANGLE_LEN 256
6871

6972
#define OFF(x) offsetof(PyCodeObject, x)
@@ -605,6 +608,8 @@ com_free(struct compiling *c)
605608
Py_XDECREF(c->c_freevars);
606609
Py_XDECREF(c->c_cellvars);
607610
Py_XDECREF(c->c_lnotab);
611+
if (c->c_future)
612+
PyMem_Free((void *)c->c_future);
608613
}
609614

610615
static void
@@ -1544,9 +1549,12 @@ com_argument(struct compiling *c, node *n, PyObject **pkeywords)
15441549
PyObject *v = PyString_InternFromString(STR(m));
15451550
if (v != NULL && *pkeywords == NULL)
15461551
*pkeywords = PyDict_New();
1547-
if (v == NULL || *pkeywords == NULL)
1552+
if (v == NULL)
15481553
c->c_errors++;
1549-
else {
1554+
else if (*pkeywords == NULL) {
1555+
c->c_errors++;
1556+
Py_DECREF(v);
1557+
} else {
15501558
if (PyDict_GetItem(*pkeywords, v) != NULL)
15511559
com_error(c, PyExc_SyntaxError,
15521560
"duplicate keyword argument");
@@ -3995,6 +4003,7 @@ symtable_build(struct compiling *c, node *n)
39954003
{
39964004
if ((c->c_symtable = symtable_init()) == NULL)
39974005
return -1;
4006+
c->c_symtable->st_future = c->c_future;
39984007
if (c->c_future->ff_nested_scopes)
39994008
c->c_symtable->st_nested_scopes = 1;
40004009
c->c_symtable->st_filename = c->c_filename;
@@ -4482,12 +4491,15 @@ symtable_add_def(struct symtable *st, char *name, int flag)
44824491
{
44834492
PyObject *s;
44844493
char buffer[MANGLE_LEN];
4494+
int ret;
44854495

44864496
if (mangle(st->st_private, name, buffer, sizeof(buffer)))
44874497
name = buffer;
44884498
if ((s = PyString_InternFromString(name)) == NULL)
44894499
return -1;
4490-
return symtable_add_def_o(st, st->st_cur->ste_symbols, s, flag);
4500+
ret = symtable_add_def_o(st, st->st_cur->ste_symbols, s, flag);
4501+
Py_DECREF(s);
4502+
return ret;
44914503
}
44924504

44934505
/* Must only be called with mangled names */
@@ -4819,12 +4831,14 @@ symtable_global(struct symtable *st, node *n)
48194831
int flags;
48204832

48214833
flags = symtable_lookup(st, name);
4834+
if (flags < 0)
4835+
continue;
48224836
if (flags && flags != DEF_GLOBAL) {
48234837
char buf[500];
48244838
if (flags & DEF_PARAM) {
48254839
PyErr_Format(PyExc_SyntaxError,
48264840
"name '%.400s' is local and global",
4827-
PyString_AS_STRING(name));
4841+
name);
48284842
set_error_location(st->st_filename,
48294843
st->st_cur->ste_lineno);
48304844
st->st_errors++;
@@ -4873,6 +4887,18 @@ symtable_import(struct symtable *st, node *n)
48734887
import_as_name: NAME [NAME NAME]
48744888
*/
48754889
if (STR(CHILD(n, 0))[0] == 'f') { /* from */
4890+
node *dotname = CHILD(n, 1);
4891+
if (strcmp(STR(CHILD(dotname, 0)), "__future__") == 0) {
4892+
/* check for bogus imports */
4893+
if (n->n_lineno >= st->st_future->ff_last_lineno) {
4894+
PyErr_SetString(PyExc_SyntaxError,
4895+
LATE_FUTURE);
4896+
set_error_location(st->st_filename,
4897+
n->n_lineno);
4898+
st->st_errors++;
4899+
return;
4900+
}
4901+
}
48764902
if (TYPE(CHILD(n, 3)) == STAR) {
48774903
st->st_cur->ste_optimized |= OPT_IMPORT_STAR;
48784904
} else {

Python/future.c

+98-25
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#define UNDEFINED_FUTURE_FEATURE "future feature %.100s is not defined"
99

10+
#define FUTURE_POSSIBLE(FF) ((FF)->ff_last_lineno == -1)
11+
1012
static int
1113
future_check_features(PyFutureFeatures *ff, node *n)
1214
{
@@ -28,6 +30,15 @@ future_check_features(PyFutureFeatures *ff, node *n)
2830
return 0;
2931
}
3032

33+
static void
34+
future_error(node *n, char *filename)
35+
{
36+
PyErr_SetString(PyExc_SyntaxError,
37+
"from __future__ imports must occur at the "
38+
"beginning of the file");
39+
/* XXX set filename and lineno */
40+
}
41+
3142
/* Relevant portions of the grammar:
3243
3344
single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
@@ -48,52 +59,82 @@ dotted_name: NAME ('.' NAME)*
4859
*/
4960

5061
static int
51-
future_parse(PyFutureFeatures *ff, node *n)
62+
future_parse(PyFutureFeatures *ff, node *n, char *filename)
5263
{
53-
int i, r, found;
64+
int i, r;
5465
loop:
5566

56-
/* fprintf(stderr, "future_parse(%d, %d, %s)\n",
57-
TYPE(n), NCH(n), (n == NULL) ? "NULL" : STR(n));
67+
/* fprintf(stderr, "future_parse(%d, %d, %s, %d)\n",
68+
TYPE(n), NCH(n), (n == NULL) ? "NULL" : STR(n),
69+
n->n_lineno);
5870
*/
71+
5972
switch (TYPE(n)) {
6073

6174
case file_input:
6275
for (i = 0; i < NCH(n); i++) {
6376
node *ch = CHILD(n, i);
6477
if (TYPE(ch) == stmt) {
65-
n = ch;
66-
goto loop;
78+
r = future_parse(ff, ch, filename);
79+
if (!FUTURE_POSSIBLE(ff))
80+
return r;
6781
}
6882
}
6983
return 0;
7084

7185
case simple_stmt:
72-
if (NCH(n) == 1) {
86+
if (NCH(n) == 2) {
7387
REQ(CHILD(n, 0), small_stmt);
7488
n = CHILD(n, 0);
7589
goto loop;
76-
}
77-
found = 0;
78-
for (i = 0; i < NCH(n); ++i)
79-
if (TYPE(CHILD(n, i)) == small_stmt) {
80-
r = future_parse(ff, CHILD(n, i));
81-
if (r < 1) {
82-
ff->ff_last_lineno = n->n_lineno;
83-
ff->ff_n_simple_stmt = i;
84-
return r;
85-
} else
86-
found++;
90+
} else {
91+
/* Deal with the special case of a series of
92+
small statements on a single line. If a
93+
future statement follows some other
94+
statement, the SyntaxError is raised here.
95+
In all other cases, the symtable pass
96+
raises the exception.
97+
*/
98+
int found = 0, end_of_future = 0;
99+
100+
for (i = 0; i < NCH(n); i += 2) {
101+
if (TYPE(CHILD(n, i)) == small_stmt) {
102+
r = future_parse(ff, CHILD(n, i),
103+
filename);
104+
if (r < 1)
105+
end_of_future = 1;
106+
else {
107+
found = 1;
108+
if (end_of_future) {
109+
future_error(n,
110+
filename);
111+
return -1;
112+
}
113+
}
114+
}
87115
}
88-
if (found)
89-
return 1;
90-
else
91-
return 0;
116+
117+
/* If we found one and only one, then the
118+
current lineno is legal.
119+
*/
120+
if (found)
121+
ff->ff_last_lineno = n->n_lineno + 1;
122+
else
123+
ff->ff_last_lineno = n->n_lineno;
124+
125+
if (end_of_future && found)
126+
return 1;
127+
else
128+
return 0;
129+
}
92130

93131
case stmt:
94132
if (TYPE(CHILD(n, 0)) == simple_stmt) {
95133
n = CHILD(n, 0);
96134
goto loop;
135+
} else if (TYPE(CHILD(n, 0)) == expr_stmt) {
136+
n = CHILD(n, 0);
137+
goto loop;
97138
} else {
98139
REQ(CHILD(n, 0), compound_stmt);
99140
ff->ff_last_lineno = n->n_lineno;
@@ -119,10 +160,42 @@ future_parse(PyFutureFeatures *ff, node *n)
119160
return 1;
120161
}
121162

163+
/* The cases below -- all of them! -- are necessary to find
164+
and skip doc strings. */
165+
case expr_stmt:
166+
case testlist:
167+
case test:
168+
case and_test:
169+
case not_test:
170+
case comparison:
171+
case expr:
172+
case xor_expr:
173+
case and_expr:
174+
case shift_expr:
175+
case arith_expr:
176+
case term:
177+
case factor:
178+
case power:
179+
if (NCH(n) == 1) {
180+
n = CHILD(n, 0);
181+
goto loop;
182+
}
183+
break;
184+
185+
case atom:
186+
if (TYPE(CHILD(n, 0)) == STRING
187+
&& ff->ff_found_docstring == 0) {
188+
ff->ff_found_docstring = 1;
189+
return 0;
190+
}
191+
ff->ff_last_lineno = n->n_lineno;
192+
return 0;
193+
122194
default:
123195
ff->ff_last_lineno = n->n_lineno;
124196
return 0;
125197
}
198+
return 0;
126199
}
127200

128201
PyFutureFeatures *
@@ -133,11 +206,11 @@ PyNode_Future(node *n, char *filename)
133206
ff = (PyFutureFeatures *)PyMem_Malloc(sizeof(PyFutureFeatures));
134207
if (ff == NULL)
135208
return NULL;
136-
ff->ff_last_lineno = 0;
137-
ff->ff_n_simple_stmt = -1;
209+
ff->ff_found_docstring = 0;
210+
ff->ff_last_lineno = -1;
138211
ff->ff_nested_scopes = 0;
139212

140-
if (future_parse(ff, n) < 0) {
213+
if (future_parse(ff, n, filename) < 0) {
141214
PyMem_Free((void *)ff);
142215
return NULL;
143216
}

0 commit comments

Comments
 (0)