* Add source location attributes to alias.
* Move alias star construction to pegen helper.
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Pablo Galindo <Pablogsal@gmail.com>
import_from_targets[asdl_alias_seq*]:
| '(' a=import_from_as_names [','] ')' { a }
| import_from_as_names !','
- | '*' { (asdl_alias_seq*)_PyPegen_singleton_seq(p, CHECK(alias_ty, _PyPegen_alias_for_star(p))) }
+ | '*' { (asdl_alias_seq*)_PyPegen_singleton_seq(p, CHECK(alias_ty, _PyPegen_alias_for_star(p, EXTRA))) }
| invalid_import_from_targets
import_from_as_names[asdl_alias_seq*]:
| a[asdl_alias_seq*]=','.import_from_as_name+ { a }
import_from_as_name[alias_ty]:
| a=NAME b=['as' z=NAME { z }] { _PyAST_alias(a->v.Name.id,
(b) ? ((expr_ty) b)->v.Name.id : NULL,
- p->arena) }
+ EXTRA) }
dotted_as_names[asdl_alias_seq*]:
| a[asdl_alias_seq*]=','.dotted_as_name+ { a }
dotted_as_name[alias_ty]:
| a=dotted_name b=['as' z=NAME { z }] { _PyAST_alias(a->v.Name.id,
(b) ? ((expr_ty) b)->v.Name.id : NULL,
- p->arena) }
+ EXTRA) }
dotted_name[expr_ty]:
| a=dotted_name '.' b=NAME { _PyPegen_join_names_with_dot(p, a, b) }
| NAME
struct _alias {
identifier name;
identifier asname;
+ int lineno;
+ int col_offset;
+ int end_lineno;
+ int end_col_offset;
};
struct _withitem {
keyword_ty _PyAST_keyword(identifier arg, expr_ty value, int lineno, int
col_offset, int end_lineno, int end_col_offset,
PyArena *arena);
-alias_ty _PyAST_alias(identifier name, identifier asname, PyArena *arena);
+alias_ty _PyAST_alias(identifier name, identifier asname, int lineno, int
+ col_offset, int end_lineno, int end_col_offset, PyArena
+ *arena);
withitem_ty _PyAST_withitem(expr_ty context_expr, expr_ty optional_vars,
PyArena *arena);
match_case_ty _PyAST_match_case(expr_ty pattern, expr_ty guard, asdl_stmt_seq *
alias = self.types['alias']
self.assertEqual(
str(alias),
- 'Product([Field(identifier, name), Field(identifier, asname, opt=True)])')
+ 'Product([Field(identifier, name), Field(identifier, asname, opt=True)], '
+ '[Field(int, lineno), Field(int, col_offset), '
+ 'Field(int, end_lineno, opt=True), Field(int, end_col_offset, opt=True)])')
def test_attributes(self):
stmt = self.types['stmt']
mod.body[0].module = " __future__ ".strip()
compile(mod, "<test>", "exec")
+ def test_alias(self):
+ im = ast.parse("from bar import y").body[0]
+ self.assertEqual(len(im.names), 1)
+ alias = im.names[0]
+ self.assertEqual(alias.name, 'y')
+ self.assertIsNone(alias.asname)
+ self.assertEqual(alias.lineno, 1)
+ self.assertEqual(alias.end_lineno, 1)
+ self.assertEqual(alias.col_offset, 16)
+ self.assertEqual(alias.end_col_offset, 17)
+
+ im = ast.parse("from bar import *").body[0]
+ alias = im.names[0]
+ self.assertEqual(alias.name, '*')
+ self.assertIsNone(alias.asname)
+ self.assertEqual(alias.lineno, 1)
+ self.assertEqual(alias.end_lineno, 1)
+ self.assertEqual(alias.col_offset, 16)
+ self.assertEqual(alias.end_col_offset, 17)
+
def test_base_classes(self):
self.assertTrue(issubclass(ast.For, ast.stmt))
self.assertTrue(issubclass(ast.Name, ast.expr))
def test_level_as_none(self):
body = [ast.ImportFrom(module='time',
- names=[ast.alias(name='sleep')],
+ names=[ast.alias(name='sleep',
+ lineno=0, col_offset=0)],
level=None,
lineno=0, col_offset=0)]
mod = ast.Module(body, [])
''').strip()
imp = ast.parse(s).body[0]
self._check_end_pos(imp, 3, 1)
+ self._check_end_pos(imp.names[2], 2, 16)
def test_slices(self):
s1 = 'f()[1, 2] [0]'
('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [('ExceptHandler', (3, 0, 4, 6), ('Name', (3, 7, 3, 16), 'Exception', ('Load',)), None, [('Pass', (4, 2, 4, 6))])], [], [])], []),
('Module', [('Try', (1, 0, 4, 6), [('Pass', (2, 2, 2, 6))], [], [], [('Pass', (4, 2, 4, 6))])], []),
('Module', [('Assert', (1, 0, 1, 8), ('Name', (1, 7, 1, 8), 'v', ('Load',)), None)], []),
-('Module', [('Import', (1, 0, 1, 10), [('alias', 'sys', None)])], []),
-('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', 'v', None)], 0)], []),
+('Module', [('Import', (1, 0, 1, 10), [('alias', (1, 7, 1, 10), 'sys', None)])], []),
+('Module', [('ImportFrom', (1, 0, 1, 17), 'sys', [('alias', (1, 16, 1, 17), 'v', None)], 0)], []),
('Module', [('Global', (1, 0, 1, 8), ['v'])], []),
('Module', [('Expr', (1, 0, 1, 1), ('Constant', (1, 0, 1, 1), 1, None))], []),
('Module', [('Pass', (1, 0, 1, 4))], []),
)
simple_name[expr_ty]: NAME
import_as_names_from[asdl_alias_seq*]: a[asdl_alias_seq*]=','.import_as_name_from+ { a }
- import_as_name_from[alias_ty]: a=NAME 'as' b=NAME { _PyAST_alias(((expr_ty) a)->v.Name.id, ((expr_ty) b)->v.Name.id, p->arena) }
+ import_as_name_from[alias_ty]: a=NAME 'as' b=NAME { _PyAST_alias(((expr_ty) a)->v.Name.id, ((expr_ty) b)->v.Name.id, EXTRA) }
"""
test_source = """
for stmt in ("from a import b as c", "from . import a as b"):
--- /dev/null
+:class:`ast.alias` nodes now include source location metadata attributes e.g. lineno, col_offset.
-- import name with optional 'as' alias.
alias = (identifier name, identifier? asname)
+ attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
withitem = (expr context_expr, expr? optional_vars)
}
asdl_alias_seq* _res = NULL;
int _mark = p->mark;
+ if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
+ p->error_indicator = 1;
+ D(p->level--);
+ return NULL;
+ }
+ int _start_lineno = p->tokens[_mark]->lineno;
+ UNUSED(_start_lineno); // Only used by EXTRA macro
+ int _start_col_offset = p->tokens[_mark]->col_offset;
+ UNUSED(_start_col_offset); // Only used by EXTRA macro
{ // '(' import_from_as_names ','? ')'
if (p->error_indicator) {
D(p->level--);
)
{
D(fprintf(stderr, "%*c+ import_from_targets[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'*'"));
- _res = ( asdl_alias_seq * ) _PyPegen_singleton_seq ( p , CHECK ( alias_ty , _PyPegen_alias_for_star ( p ) ) );
+ Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
+ if (_token == NULL) {
+ D(p->level--);
+ return NULL;
+ }
+ int _end_lineno = _token->end_lineno;
+ UNUSED(_end_lineno); // Only used by EXTRA macro
+ int _end_col_offset = _token->end_col_offset;
+ UNUSED(_end_col_offset); // Only used by EXTRA macro
+ _res = ( asdl_alias_seq * ) _PyPegen_singleton_seq ( p , CHECK ( alias_ty , _PyPegen_alias_for_star ( p , EXTRA ) ) );
if (_res == NULL && PyErr_Occurred()) {
p->error_indicator = 1;
D(p->level--);
}
alias_ty _res = NULL;
int _mark = p->mark;
+ if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
+ p->error_indicator = 1;
+ D(p->level--);
+ return NULL;
+ }
+ int _start_lineno = p->tokens[_mark]->lineno;
+ UNUSED(_start_lineno); // Only used by EXTRA macro
+ int _start_col_offset = p->tokens[_mark]->col_offset;
+ UNUSED(_start_col_offset); // Only used by EXTRA macro
{ // NAME ['as' NAME]
if (p->error_indicator) {
D(p->level--);
)
{
D(fprintf(stderr, "%*c+ import_from_as_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME ['as' NAME]"));
- _res = _PyAST_alias ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Name . id : NULL , p -> arena );
+ Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
+ if (_token == NULL) {
+ D(p->level--);
+ return NULL;
+ }
+ int _end_lineno = _token->end_lineno;
+ UNUSED(_end_lineno); // Only used by EXTRA macro
+ int _end_col_offset = _token->end_col_offset;
+ UNUSED(_end_col_offset); // Only used by EXTRA macro
+ _res = _PyAST_alias ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Name . id : NULL , EXTRA );
if (_res == NULL && PyErr_Occurred()) {
p->error_indicator = 1;
D(p->level--);
}
alias_ty _res = NULL;
int _mark = p->mark;
+ if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {
+ p->error_indicator = 1;
+ D(p->level--);
+ return NULL;
+ }
+ int _start_lineno = p->tokens[_mark]->lineno;
+ UNUSED(_start_lineno); // Only used by EXTRA macro
+ int _start_col_offset = p->tokens[_mark]->col_offset;
+ UNUSED(_start_col_offset); // Only used by EXTRA macro
{ // dotted_name ['as' NAME]
if (p->error_indicator) {
D(p->level--);
)
{
D(fprintf(stderr, "%*c+ dotted_as_name[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "dotted_name ['as' NAME]"));
- _res = _PyAST_alias ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Name . id : NULL , p -> arena );
+ Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
+ if (_token == NULL) {
+ D(p->level--);
+ return NULL;
+ }
+ int _end_lineno = _token->end_lineno;
+ UNUSED(_end_lineno); // Only used by EXTRA macro
+ int _end_col_offset = _token->end_col_offset;
+ UNUSED(_end_col_offset); // Only used by EXTRA macro
+ _res = _PyAST_alias ( a -> v . Name . id , ( b ) ? ( ( expr_ty ) b ) -> v . Name . id : NULL , EXTRA );
if (_res == NULL && PyErr_Occurred()) {
p->error_indicator = 1;
D(p->level--);
/* Creates an alias with '*' as the identifier name */
alias_ty
-_PyPegen_alias_for_star(Parser *p)
-{
+_PyPegen_alias_for_star(Parser *p, int lineno, int col_offset, int end_lineno,
+ int end_col_offset, PyArena *arena) {
PyObject *str = PyUnicode_InternFromString("*");
if (!str) {
return NULL;
Py_DECREF(str);
return NULL;
}
- return _PyAST_alias(str, NULL, p->arena);
+ return _PyAST_alias(str, NULL, lineno, col_offset, end_lineno, end_col_offset, arena);
}
/* Creates a new asdl_seq* with the identifiers of all the names in seq */
asdl_seq *_PyPegen_seq_flatten(Parser *, asdl_seq *);
expr_ty _PyPegen_join_names_with_dot(Parser *, expr_ty, expr_ty);
int _PyPegen_seq_count_dots(asdl_seq *);
-alias_ty _PyPegen_alias_for_star(Parser *);
+alias_ty _PyPegen_alias_for_star(Parser *, int, int, int, int, PyArena *);
asdl_identifier_seq *_PyPegen_map_names_to_ids(Parser *, asdl_expr_seq *);
CmpopExprPair *_PyPegen_cmpop_expr_pair(Parser *, cmpop_ty, expr_ty);
asdl_int_seq *_PyPegen_get_cmpops(Parser *p, asdl_seq *);
"value",
};
static PyObject* ast2obj_alias(struct ast_state *state, void*);
+static const char * const alias_attributes[] = {
+ "lineno",
+ "col_offset",
+ "end_lineno",
+ "end_col_offset",
+};
static const char * const alias_fields[]={
"name",
"asname",
alias_fields, 2,
"alias(identifier name, identifier? asname)");
if (!state->alias_type) return 0;
- if (!add_attributes(state, state->alias_type, NULL, 0)) return 0;
+ if (!add_attributes(state, state->alias_type, alias_attributes, 4)) return
+ 0;
if (PyObject_SetAttr(state->alias_type, state->asname, Py_None) == -1)
return 0;
+ if (PyObject_SetAttr(state->alias_type, state->end_lineno, Py_None) == -1)
+ return 0;
+ if (PyObject_SetAttr(state->alias_type, state->end_col_offset, Py_None) ==
+ -1)
+ return 0;
state->withitem_type = make_type(state, "withitem", state->AST_type,
withitem_fields, 2,
"withitem(expr context_expr, expr? optional_vars)");
}
alias_ty
-_PyAST_alias(identifier name, identifier asname, PyArena *arena)
+_PyAST_alias(identifier name, identifier asname, int lineno, int col_offset,
+ int end_lineno, int end_col_offset, PyArena *arena)
{
alias_ty p;
if (!name) {
return NULL;
p->name = name;
p->asname = asname;
+ p->lineno = lineno;
+ p->col_offset = col_offset;
+ p->end_lineno = end_lineno;
+ p->end_col_offset = end_col_offset;
return p;
}
if (PyObject_SetAttr(result, state->asname, value) == -1)
goto failed;
Py_DECREF(value);
+ value = ast2obj_int(state, o->lineno);
+ if (!value) goto failed;
+ if (PyObject_SetAttr(result, state->lineno, value) < 0)
+ goto failed;
+ Py_DECREF(value);
+ value = ast2obj_int(state, o->col_offset);
+ if (!value) goto failed;
+ if (PyObject_SetAttr(result, state->col_offset, value) < 0)
+ goto failed;
+ Py_DECREF(value);
+ value = ast2obj_int(state, o->end_lineno);
+ if (!value) goto failed;
+ if (PyObject_SetAttr(result, state->end_lineno, value) < 0)
+ goto failed;
+ Py_DECREF(value);
+ value = ast2obj_int(state, o->end_col_offset);
+ if (!value) goto failed;
+ if (PyObject_SetAttr(result, state->end_col_offset, value) < 0)
+ goto failed;
+ Py_DECREF(value);
return result;
failed:
Py_XDECREF(value);
PyObject* tmp = NULL;
identifier name;
identifier asname;
+ int lineno;
+ int col_offset;
+ int end_lineno;
+ int end_col_offset;
if (_PyObject_LookupAttr(obj, state->name, &tmp) < 0) {
return 1;
if (res != 0) goto failed;
Py_CLEAR(tmp);
}
- *out = _PyAST_alias(name, asname, arena);
+ if (_PyObject_LookupAttr(obj, state->lineno, &tmp) < 0) {
+ return 1;
+ }
+ if (tmp == NULL) {
+ PyErr_SetString(PyExc_TypeError, "required field \"lineno\" missing from alias");
+ return 1;
+ }
+ else {
+ int res;
+ res = obj2ast_int(state, tmp, &lineno, arena);
+ if (res != 0) goto failed;
+ Py_CLEAR(tmp);
+ }
+ if (_PyObject_LookupAttr(obj, state->col_offset, &tmp) < 0) {
+ return 1;
+ }
+ if (tmp == NULL) {
+ PyErr_SetString(PyExc_TypeError, "required field \"col_offset\" missing from alias");
+ return 1;
+ }
+ else {
+ int res;
+ res = obj2ast_int(state, tmp, &col_offset, arena);
+ if (res != 0) goto failed;
+ Py_CLEAR(tmp);
+ }
+ if (_PyObject_LookupAttr(obj, state->end_lineno, &tmp) < 0) {
+ return 1;
+ }
+ if (tmp == NULL || tmp == Py_None) {
+ Py_CLEAR(tmp);
+ end_lineno = 0;
+ }
+ else {
+ int res;
+ res = obj2ast_int(state, tmp, &end_lineno, arena);
+ if (res != 0) goto failed;
+ Py_CLEAR(tmp);
+ }
+ if (_PyObject_LookupAttr(obj, state->end_col_offset, &tmp) < 0) {
+ return 1;
+ }
+ if (tmp == NULL || tmp == Py_None) {
+ Py_CLEAR(tmp);
+ end_col_offset = 0;
+ }
+ else {
+ int res;
+ res = obj2ast_int(state, tmp, &end_col_offset, arena);
+ if (res != 0) goto failed;
+ Py_CLEAR(tmp);
+ }
+ *out = _PyAST_alias(name, asname, lineno, col_offset, end_lineno,
+ end_col_offset, arena);
return 0;
failed:
Py_XDECREF(tmp);