Commit ca6c6743 authored by Robert Bradshaw's avatar Robert Bradshaw

More buffer type analysis deferment.

parent c0fb8492
...@@ -137,7 +137,7 @@ def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, nee ...@@ -137,7 +137,7 @@ def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, nee
if defaults is None: if defaults is None:
defaults = buffer_defaults defaults = buffer_defaults
posargs, dictargs = Interpreter.interpret_compiletime_options(posargs, dictargs, type_env=env) posargs, dictargs = Interpreter.interpret_compiletime_options(posargs, dictargs, type_env=env, type_args = (0,'dtype'))
if len(posargs) > buffer_positional_options_count: if len(posargs) > buffer_positional_options_count:
raise CompileError(posargs[-1][1], ERR_BUF_TOO_MANY) raise CompileError(posargs[-1][1], ERR_BUF_TOO_MANY)
......
...@@ -17,7 +17,7 @@ class EmptyScope(object): ...@@ -17,7 +17,7 @@ class EmptyScope(object):
empty_scope = EmptyScope() empty_scope = EmptyScope()
def interpret_compiletime_options(optlist, optdict, type_env=None): def interpret_compiletime_options(optlist, optdict, type_env=None, type_args=()):
""" """
Tries to interpret a list of compile time option nodes. Tries to interpret a list of compile time option nodes.
The result will be a tuple (optlist, optdict) but where The result will be a tuple (optlist, optdict) but where
...@@ -34,21 +34,21 @@ def interpret_compiletime_options(optlist, optdict, type_env=None): ...@@ -34,21 +34,21 @@ def interpret_compiletime_options(optlist, optdict, type_env=None):
A CompileError will be raised if there are problems. A CompileError will be raised if there are problems.
""" """
def interpret(node): def interpret(node, ix):
if isinstance(node, CBaseTypeNode): if ix in type_args:
if type_env: if type_env:
return (node.analyse(type_env), node.pos) return (node.analyse_as_type(type_env), node.pos)
else: else:
raise CompileError(node.pos, "Type not allowed here.") raise CompileError(node.pos, "Type not allowed here.")
else: else:
return (node.compile_time_value(empty_scope), node.pos) return (node.compile_time_value(empty_scope), node.pos)
if optlist: if optlist:
optlist = [interpret(x) for x in optlist] optlist = [interpret(x, ix) for ix, x in enumerate(optlist)]
if optdict: if optdict:
assert isinstance(optdict, DictNode) assert isinstance(optdict, DictNode)
new_optdict = {} new_optdict = {}
for item in optdict.key_value_pairs: for item in optdict.key_value_pairs:
new_optdict[item.key.value] = interpret(item.value) new_optdict[item.key.value] = interpret(item.value, item.key.value)
optdict = new_optdict optdict = new_optdict
return (optlist, new_optdict) return (optlist, new_optdict)
...@@ -791,6 +791,7 @@ class TemplatedTypeNode(CBaseTypeNode): ...@@ -791,6 +791,7 @@ class TemplatedTypeNode(CBaseTypeNode):
if base_type.is_error: return base_type if base_type.is_error: return base_type
if base_type.is_cpp_class: if base_type.is_cpp_class:
# Templated class
if len(self.keyword_args.key_value_pairs) != 0: if len(self.keyword_args.key_value_pairs) != 0:
error(self.pos, "c++ templates cannot take keyword arguments"); error(self.pos, "c++ templates cannot take keyword arguments");
self.type = PyrexTypes.error_type self.type = PyrexTypes.error_type
...@@ -800,8 +801,8 @@ class TemplatedTypeNode(CBaseTypeNode): ...@@ -800,8 +801,8 @@ class TemplatedTypeNode(CBaseTypeNode):
template_types.append(template_node.analyse_as_type(env)) template_types.append(template_node.analyse_as_type(env))
self.type = base_type.specialize_here(self.pos, template_types) self.type = base_type.specialize_here(self.pos, template_types)
else: elif base_type.is_pyobject:
# Buffer
import Buffer import Buffer
options = Buffer.analyse_buffer_options( options = Buffer.analyse_buffer_options(
...@@ -817,6 +818,24 @@ class TemplatedTypeNode(CBaseTypeNode): ...@@ -817,6 +818,24 @@ class TemplatedTypeNode(CBaseTypeNode):
for name, value in options.iteritems() ]) for name, value in options.iteritems() ])
self.type = PyrexTypes.BufferType(base_type, **options) self.type = PyrexTypes.BufferType(base_type, **options)
else:
# Array
empty_declarator = CNameDeclaratorNode(self.pos, name="")
if len(self.positional_args) > 1 or self.keyword_args.key_value_pairs:
error(self.pos, "invalid array declaration")
self.type = PyrexTypes.error_type
else:
# It would be nice to merge this class with CArrayDeclaratorNode,
# but arrays are part of the declaration, not the type...
if not self.positional_args:
dimension = None
else:
dimension = self.positional_args[0]
self.type = CArrayDeclaratorNode(self.pos,
base = empty_declarator,
dimension = dimension).analyse(base_type, env)[1]
return self.type return self.type
class CComplexBaseTypeNode(CBaseTypeNode): class CComplexBaseTypeNode(CBaseTypeNode):
......
...@@ -100,13 +100,13 @@ cpdef p_IF_statement(PyrexScanner s, ctx) ...@@ -100,13 +100,13 @@ cpdef p_IF_statement(PyrexScanner s, ctx)
cpdef p_statement(PyrexScanner s, ctx, bint first_statement = *) cpdef p_statement(PyrexScanner s, ctx, bint first_statement = *)
cpdef p_statement_list(PyrexScanner s, ctx, bint first_statement = *) cpdef p_statement_list(PyrexScanner s, ctx, bint first_statement = *)
cpdef p_suite(PyrexScanner s, ctx = *, bint with_doc = *, bint with_pseudo_doc = *) cpdef p_suite(PyrexScanner s, ctx = *, bint with_doc = *, bint with_pseudo_doc = *)
cpdef p_positional_and_keyword_args(PyrexScanner s, end_sy_set, type_positions= *, type_keywords= * ) cpdef p_positional_and_keyword_args(PyrexScanner s, end_sy_set, templates = *)
cpdef p_c_base_type(PyrexScanner s, bint self_flag = *, bint nonempty = *, templates = *) cpdef p_c_base_type(PyrexScanner s, bint self_flag = *, bint nonempty = *, templates = *)
cpdef p_calling_convention(PyrexScanner s) cpdef p_calling_convention(PyrexScanner s)
cpdef p_c_complex_base_type(PyrexScanner s) cpdef p_c_complex_base_type(PyrexScanner s)
cpdef p_c_simple_base_type(PyrexScanner s, bint self_flag, bint nonempty, templates = *) cpdef p_c_simple_base_type(PyrexScanner s, bint self_flag, bint nonempty, templates = *)
cpdef p_buffer_or_template(PyrexScanner s, base_type_node) cpdef p_buffer_or_template(PyrexScanner s, base_type_node, templates)
cpdef bint looking_at_name(PyrexScanner s) except -2 cpdef bint looking_at_name(PyrexScanner s) except -2
cpdef bint looking_at_expr(PyrexScanner s) except -2 cpdef bint looking_at_expr(PyrexScanner s) except -2
cpdef bint looking_at_base_type(PyrexScanner s) except -2 cpdef bint looking_at_base_type(PyrexScanner s) except -2
......
...@@ -1723,18 +1723,12 @@ def p_suite(s, ctx = Ctx(), with_doc = 0, with_pseudo_doc = 0): ...@@ -1723,18 +1723,12 @@ def p_suite(s, ctx = Ctx(), with_doc = 0, with_pseudo_doc = 0):
else: else:
return body return body
def p_positional_and_keyword_args(s, end_sy_set, type_positions=(), type_keywords=()): def p_positional_and_keyword_args(s, end_sy_set, templates = None):
""" """
Parses positional and keyword arguments. end_sy_set Parses positional and keyword arguments. end_sy_set
should contain any s.sy that terminate the argument list. should contain any s.sy that terminate the argument list.
Argument expansion (* and **) are not allowed. Argument expansion (* and **) are not allowed.
type_positions and type_keywords specifies which argument
positions and/or names which should be interpreted as
types. Other arguments will be treated as expressions.
A value of None indicates all positions/keywords
(respectively) will be treated as types.
Returns: (positional_args, keyword_args) Returns: (positional_args, keyword_args)
""" """
positional_args = [] positional_args = []
...@@ -1745,34 +1739,33 @@ def p_positional_and_keyword_args(s, end_sy_set, type_positions=(), type_keyword ...@@ -1745,34 +1739,33 @@ def p_positional_and_keyword_args(s, end_sy_set, type_positions=(), type_keyword
if s.sy == '*' or s.sy == '**': if s.sy == '*' or s.sy == '**':
s.error('Argument expansion not allowed here.') s.error('Argument expansion not allowed here.')
was_keyword = False
parsed_type = False parsed_type = False
if s.sy == 'IDENT' and s.peek()[0] == '=': if s.sy == 'IDENT' and s.peek()[0] == '=':
ident = s.systring ident = s.systring
s.next() # s.sy is '=' s.next() # s.sy is '='
s.next() s.next()
if type_keywords is None or ident in type_keywords: if looking_at_expr(s):
base_type = p_c_base_type(s) arg = p_simple_expr(s)
else:
base_type = p_c_base_type(s, templates = templates)
declarator = p_c_declarator(s, empty = 1) declarator = p_c_declarator(s, empty = 1)
arg = Nodes.CComplexBaseTypeNode(base_type.pos, arg = Nodes.CComplexBaseTypeNode(base_type.pos,
base_type = base_type, declarator = declarator) base_type = base_type, declarator = declarator)
parsed_type = True parsed_type = True
else:
arg = p_simple_expr(s)
keyword_node = ExprNodes.IdentifierStringNode( keyword_node = ExprNodes.IdentifierStringNode(
arg.pos, value = EncodedString(ident)) arg.pos, value = EncodedString(ident))
keyword_args.append((keyword_node, arg)) keyword_args.append((keyword_node, arg))
was_keyword = True was_keyword = True
else: else:
if type_positions is None or pos_idx in type_positions: if looking_at_expr(s):
base_type = p_c_base_type(s) arg = p_simple_expr(s)
else:
base_type = p_c_base_type(s, templates = templates)
declarator = p_c_declarator(s, empty = 1) declarator = p_c_declarator(s, empty = 1)
arg = Nodes.CComplexBaseTypeNode(base_type.pos, arg = Nodes.CComplexBaseTypeNode(base_type.pos,
base_type = base_type, declarator = declarator) base_type = base_type, declarator = declarator)
parsed_type = True parsed_type = True
else:
arg = p_simple_expr(s)
positional_args.append(arg) positional_args.append(arg)
pos_idx += 1 pos_idx += 1
if len(keyword_args) > 0: if len(keyword_args) > 0:
...@@ -1782,9 +1775,7 @@ def p_positional_and_keyword_args(s, end_sy_set, type_positions=(), type_keyword ...@@ -1782,9 +1775,7 @@ def p_positional_and_keyword_args(s, end_sy_set, type_positions=(), type_keyword
if s.sy != ',': if s.sy != ',':
if s.sy not in end_sy_set: if s.sy not in end_sy_set:
if parsed_type: if parsed_type:
s.error("Expected: type") s.error("Unmatched %s" % " or ".join(end_sy_set))
else:
s.error("Expected: expression")
break break
s.next() s.next()
return positional_args, keyword_args return positional_args, keyword_args
...@@ -1876,25 +1867,19 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None): ...@@ -1876,25 +1867,19 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
is_self_arg = self_flag, templates = templates) is_self_arg = self_flag, templates = templates)
# Treat trailing [] on type as buffer access if it appears in a context if s.sy == '[':
# where declarator names are required (so that it cannot mean int[] or return p_buffer_or_template(s, type_node, templates)
# sizeof(int[SIZE]))...
#
# (This means that buffers cannot occur where there can be empty declarators,
# which is an ok restriction to make.)
if nonempty and s.sy == '[':
return p_buffer_or_template(s, type_node)
else: else:
return type_node return type_node
def p_buffer_or_template(s, base_type_node): def p_buffer_or_template(s, base_type_node, templates):
# s.sy == '[' # s.sy == '['
pos = s.position() pos = s.position()
s.next() s.next()
# Note that buffer_positional_options_count=1, so the only positional argument is dtype. # Note that buffer_positional_options_count=1, so the only positional argument is dtype.
# For templated types, all parameters are types. # For templated types, all parameters are types.
positional_args, keyword_args = ( positional_args, keyword_args = (
p_positional_and_keyword_args(s, (']',), None, ('dtype',)) p_positional_and_keyword_args(s, (']',), templates)
) )
s.expect(']') s.expect(']')
......
...@@ -32,10 +32,6 @@ class TestBufferParsing(CythonTest): ...@@ -32,10 +32,6 @@ class TestBufferParsing(CythonTest):
def test_type_keyword(self): def test_type_keyword(self):
self.parse(u"cdef object[foo=foo, dtype=short unsigned int] x") self.parse(u"cdef object[foo=foo, dtype=short unsigned int] x")
def test_notype_as_expr1(self):
self.not_parseable("Expected: expression",
u"cdef object[foo2=short unsigned int] x")
def test_pos_after_key(self): def test_pos_after_key(self):
self.not_parseable("Non-keyword arg following keyword arg", self.not_parseable("Non-keyword arg following keyword arg",
u"cdef object[foo=1, 2] x") u"cdef object[foo=1, 2] x")
......
...@@ -14,7 +14,7 @@ def f(): ...@@ -14,7 +14,7 @@ def f():
_ERRORS = u""" _ERRORS = u"""
1:17: Buffer types only allowed as function local variables 1:17: Buffer types only allowed as function local variables
3:21: Buffer types only allowed as function local variables 3:21: Buffer types only allowed as function local variables
6:27: "fakeoption" is not a buffer option 6:31: "fakeoption" is not a buffer option
""" """
#TODO: #TODO:
#7:22: "ndim" must be non-negative #7:22: "ndim" must be non-negative
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment