mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-05 17:00:58 +01:00
first go at new resolution for additional syntax errors
This commit is contained in:
parent
c30000f9c9
commit
4ff14f5094
2 changed files with 84 additions and 0 deletions
|
@ -17,6 +17,7 @@ from xonsh.lexer import Lexer, LexToken
|
||||||
from xonsh.platform import PYTHON_VERSION_INFO
|
from xonsh.platform import PYTHON_VERSION_INFO
|
||||||
from xonsh.tokenize import SearchPath
|
from xonsh.tokenize import SearchPath
|
||||||
from xonsh.lazyasd import LazyObject
|
from xonsh.lazyasd import LazyObject
|
||||||
|
from xonsh.parsers.context_check import check_contexts
|
||||||
|
|
||||||
RE_SEARCHPATH = LazyObject(lambda: re.compile(SearchPath), globals(),
|
RE_SEARCHPATH = LazyObject(lambda: re.compile(SearchPath), globals(),
|
||||||
'RE_SEARCHPATH')
|
'RE_SEARCHPATH')
|
||||||
|
@ -309,6 +310,8 @@ class BaseParser(object):
|
||||||
while self.parser is None:
|
while self.parser is None:
|
||||||
time.sleep(0.01) # block until the parser is ready
|
time.sleep(0.01) # block until the parser is ready
|
||||||
tree = self.parser.parse(input=s, lexer=self.lexer, debug=debug_level)
|
tree = self.parser.parse(input=s, lexer=self.lexer, debug=debug_level)
|
||||||
|
if tree is not None:
|
||||||
|
check_contexts(tree)
|
||||||
# hack for getting modes right
|
# hack for getting modes right
|
||||||
if mode == 'single':
|
if mode == 'single':
|
||||||
if isinstance(tree, ast.Expression):
|
if isinstance(tree, ast.Expression):
|
||||||
|
|
81
xonsh/parsers/context_check.py
Normal file
81
xonsh/parsers/context_check.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
import ast
|
||||||
|
import keyword
|
||||||
|
import collections
|
||||||
|
|
||||||
|
_all_keywords = frozenset(keyword.kwlist)
|
||||||
|
|
||||||
|
def _not_assignable(x, augassign=False):
|
||||||
|
"""
|
||||||
|
If ``x`` represents a value that can be assigned to, return ``None``.
|
||||||
|
Otherwise, return a string describing the object. For use in generating
|
||||||
|
meaningful syntax errors.
|
||||||
|
"""
|
||||||
|
if augassign and isinstance(x, (ast.Tuple, ast.List)):
|
||||||
|
return 'literal'
|
||||||
|
elif isinstance(x, (ast.Tuple, ast.List)):
|
||||||
|
if len(x.elts) == 0:
|
||||||
|
return '()'
|
||||||
|
for i in x.elts:
|
||||||
|
res = _not_assignable(i)
|
||||||
|
if res is not None:
|
||||||
|
return res
|
||||||
|
elif isinstance(x, (ast.Set, ast.Dict, ast.Num, ast.Str, ast.Bytes)):
|
||||||
|
return 'literal'
|
||||||
|
elif isinstance(x, ast.Call):
|
||||||
|
return 'function call'
|
||||||
|
elif isinstance(x, ast.Lambda):
|
||||||
|
return 'lambda'
|
||||||
|
elif isinstance(x, (ast.BoolOp, ast.BinOp, ast.UnaryOp)):
|
||||||
|
return 'operator'
|
||||||
|
elif isinstance(x, ast.IfExp):
|
||||||
|
return 'conditional expression'
|
||||||
|
elif isinstance(x, ast.ListComp):
|
||||||
|
return 'list comprehension'
|
||||||
|
elif isinstance(x, ast.DictComp):
|
||||||
|
return 'dictionary comprehension'
|
||||||
|
elif isinstance(x, ast.SetComp):
|
||||||
|
return 'set comprehension'
|
||||||
|
elif isinstance(x, ast.GeneratorExp):
|
||||||
|
return 'generator expression'
|
||||||
|
elif isinstance(x, ast.Compare):
|
||||||
|
return 'comparison'
|
||||||
|
elif isinstance(x, ast.Name) and x.id in _all_keywords:
|
||||||
|
return 'keyword'
|
||||||
|
elif isinstance(x, ast.NameConstant):
|
||||||
|
return 'keyword'
|
||||||
|
|
||||||
|
_loc = collections.namedtuple('_loc', ['lineno', 'column'])
|
||||||
|
|
||||||
|
def check_contexts(tree):
|
||||||
|
c = ContextCheckingVisitor()
|
||||||
|
c.visit(tree)
|
||||||
|
if c.error is not None:
|
||||||
|
e = SyntaxError(c.error[0])
|
||||||
|
e.loc = _loc(c.error[1], c.error[2])
|
||||||
|
raise e
|
||||||
|
|
||||||
|
class ContextCheckingVisitor(ast.NodeVisitor):
|
||||||
|
def __init__(self):
|
||||||
|
self.error = None
|
||||||
|
|
||||||
|
def visit_Delete(self, node):
|
||||||
|
for i in node.targets:
|
||||||
|
err = _not_assignable(i)
|
||||||
|
if err is not None:
|
||||||
|
msg = "can't delete {}".format(err)
|
||||||
|
self.error = msg, i.lineno, i.col_offset
|
||||||
|
break
|
||||||
|
|
||||||
|
def visit_Assign(self, node):
|
||||||
|
for i in node.targets:
|
||||||
|
err = _not_assignable(i)
|
||||||
|
if err is not None:
|
||||||
|
msg = "can't assign to {}".format(err)
|
||||||
|
self.error = msg, i.lineno, i.col_offset
|
||||||
|
break
|
||||||
|
|
||||||
|
def visit_AugAssign(self, node):
|
||||||
|
err = _not_assignable(node.target, True)
|
||||||
|
if err is not None:
|
||||||
|
msg = "illegal target for augmented assignment: {}".format(err)
|
||||||
|
self.error = msg, node.target.lineno, node.target.col_offset
|
Loading…
Add table
Reference in a new issue