summaryrefslogtreecommitdiffstats
path: root/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Parsing.py
diff options
context:
space:
mode:
Diffstat (limited to 'debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Parsing.py')
-rw-r--r--debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Parsing.py2142
1 files changed, 2142 insertions, 0 deletions
diff --git a/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Parsing.py b/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Parsing.py
new file mode 100644
index 00000000..b7b9e4b5
--- /dev/null
+++ b/debian/pyrex/pyrex-0.9.9/Pyrex/Compiler/Parsing.py
@@ -0,0 +1,2142 @@
+#
+# Pyrex Parser
+#
+
+import os, re
+from string import join, replace
+from types import ListType, TupleType
+from Scanning import PyrexScanner
+import Nodes
+import ExprNodes
+from ModuleNode import ModuleNode
+from Errors import warning, error, InternalError
+
+
+class Ctx(object):
+ # Parsing context
+ level = 'other'
+ visibility = 'private'
+ extern_from = False
+ cdef_flag = 0
+ cplus_flag = 0
+ typedef_flag = 0
+ api = 0
+ nogil = 0
+
+ def __init__(self, **kwds):
+ self.__dict__.update(kwds)
+
+ def __call__(self, **kwds):
+ ctx = Ctx()
+ d = ctx.__dict__
+ d.update(self.__dict__)
+ d.update(kwds)
+ return ctx
+
+ def cplus_check(self, pos):
+ #if self.visibility <> 'extern':
+ # error(pos, "C++ declarations must be 'extern'")
+ if self.cplus_flag and not self.extern_from:
+ error(pos, "C++ declarations must be in an 'extern from' block")
+
+
+def p_ident(s, message = "Expected an identifier"):
+ if s.sy == 'IDENT':
+ name = s.systring
+ s.next()
+ return name
+ else:
+ s.error(message)
+
+def p_ident_list(s):
+ names = []
+ while s.sy == 'IDENT':
+ names.append(s.systring)
+ s.next()
+ if s.sy <> ',':
+ break
+ s.next()
+ return names
+
+#------------------------------------------
+#
+# Expressions
+#
+#------------------------------------------
+
+def p_binop_expr(s, ops, p_sub_expr):
+ n1 = p_sub_expr(s)
+ while s.sy in ops:
+ op = s.sy
+ pos = s.position()
+ s.next()
+ n2 = p_sub_expr(s)
+ n1 = ExprNodes.binop_node(pos, op, n1, n2)
+ return n1
+
+#test: and_test ('or' and_test)* | lambdef
+
+def p_simple_expr(s):
+ return p_rassoc_binop_expr(s, ('or',), p_and_test)
+
+def p_rassoc_binop_expr(s, ops, p_subexpr):
+ n1 = p_subexpr(s)
+ if s.sy in ops:
+ pos = s.position()
+ op = s.sy
+ s.next()
+ n2 = p_rassoc_binop_expr(s, ops, p_subexpr)
+ n1 = ExprNodes.binop_node(pos, op, n1, n2)
+ return n1
+
+#and_test: not_test ('and' not_test)*
+
+def p_and_test(s):
+ #return p_binop_expr(s, ('and',), p_not_test)
+ return p_rassoc_binop_expr(s, ('and',), p_not_test)
+
+#not_test: 'not' not_test | comparison
+
+def p_not_test(s):
+ if s.sy == 'not':
+ pos = s.position()
+ s.next()
+ return ExprNodes.NotNode(pos, operand = p_not_test(s))
+ else:
+ return p_comparison(s)
+
+#comparison: expr (comp_op expr)*
+#comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
+
+def p_comparison(s):
+ n1 = p_bit_expr(s)
+ if s.sy in comparison_ops:
+ pos = s.position()
+ op = p_cmp_op(s)
+ n2 = p_bit_expr(s)
+ n1 = ExprNodes.PrimaryCmpNode(pos,
+ operator = op, operand1 = n1, operand2 = n2)
+ if s.sy in comparison_ops:
+ n1.cascade = p_cascaded_cmp(s)
+ return n1
+
+def p_cascaded_cmp(s):
+ pos = s.position()
+ op = p_cmp_op(s)
+ n2 = p_bit_expr(s)
+ result = ExprNodes.CascadedCmpNode(pos,
+ operator = op, operand2 = n2)
+ if s.sy in comparison_ops:
+ result.cascade = p_cascaded_cmp(s)
+ return result
+
+def p_cmp_op(s):
+ if s.sy == 'not':
+ s.next()
+ s.expect('in')
+ op = 'not_in'
+ elif s.sy == 'is':
+ s.next()
+ if s.sy == 'not':
+ s.next()
+ op = 'is_not'
+ else:
+ op = 'is'
+ else:
+ op = s.sy
+ s.next()
+ if op == '<>':
+ op = '!='
+ return op
+
+comparison_ops = (
+ '<', '>', '==', '>=', '<=', '<>', '!=',
+ 'in', 'is', 'not'
+)
+
+#expr: xor_expr ('|' xor_expr)*
+
+def p_bit_expr(s):
+ return p_binop_expr(s, ('|',), p_xor_expr)
+
+#xor_expr: and_expr ('^' and_expr)*
+
+def p_xor_expr(s):
+ return p_binop_expr(s, ('^',), p_and_expr)
+
+#and_expr: shift_expr ('&' shift_expr)*
+
+def p_and_expr(s):
+ return p_binop_expr(s, ('&',), p_shift_expr)
+
+#shift_expr: arith_expr (('<<'|'>>') arith_expr)*
+
+def p_shift_expr(s):
+ return p_binop_expr(s, ('<<', '>>'), p_arith_expr)
+
+#arith_expr: term (('+'|'-') term)*
+
+def p_arith_expr(s):
+ return p_binop_expr(s, ('+', '-'), p_term)
+
+#term: factor (('*'|'/'|'%') factor)*
+
+def p_term(s):
+ return p_binop_expr(s, ('*', '/', '%'), p_factor)
+
+#factor: ('+'|'-'|'~'|'&'|typecast|sizeof) factor | power
+
+def p_factor(s):
+ sy = s.sy
+ if sy in ('+', '-', '~'):
+ op = s.sy
+ pos = s.position()
+ s.next()
+ return ExprNodes.unop_node(pos, op, p_factor(s))
+ elif sy == '&':
+ pos = s.position()
+ s.next()
+ arg = p_factor(s)
+ return ExprNodes.AmpersandNode(pos, operand = arg)
+ elif sy == "<":
+ return p_typecast(s)
+ elif sy == 'IDENT' and s.systring == "sizeof":
+ return p_sizeof(s)
+ else:
+ return p_power(s)
+
+def p_typecast(s):
+ # s.sy == "<"
+ pos = s.position()
+ s.next()
+ base_type = p_c_base_type(s)
+ declarator = p_c_declarator(s, empty = 1)
+ s.expect(">")
+ operand = p_factor(s)
+ return ExprNodes.TypecastNode(pos,
+ base_type = base_type,
+ declarator = declarator,
+ operand = operand)
+
+def p_sizeof(s):
+ # s.sy == ident "sizeof"
+ pos = s.position()
+ s.next()
+ s.expect('(')
+ if looking_at_type(s):
+ base_type = p_c_base_type(s)
+ declarator = p_c_declarator(s, empty = 1)
+ node = ExprNodes.SizeofTypeNode(pos,
+ base_type = base_type, declarator = declarator)
+ else:
+ operand = p_simple_expr(s)
+ node = ExprNodes.SizeofVarNode(pos, operand = operand)
+ s.expect(')')
+ return node
+
+#power: atom trailer* ('**' factor)*
+
+def p_power(s):
+ n1 = p_primitive(s)
+ if s.sy == '**':
+ pos = s.position()
+ s.next()
+ n2 = p_factor(s)
+ n1 = ExprNodes.binop_node(pos, '**', n1, n2)
+ return n1
+
+def p_primitive(s):
+ n = p_atom(s)
+ while s.sy in ('(', '[', '.'):
+ n = p_trailer(s, n)
+ return n
+
+#trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
+
+def p_trailer(s, node1):
+ pos = s.position()
+ if s.sy == '(':
+ return p_call(s, node1)
+ elif s.sy == '[':
+ return p_index(s, node1)
+ else: # s.sy == '.'
+ s.next()
+ name = p_ident(s)
+ return ExprNodes.AttributeNode(pos,
+ obj = node1, attribute = name)
+
+# arglist: argument (',' argument)* [',']
+# argument: [test '='] test # Really [keyword '='] test
+
+def p_call(s, function):
+ # s.sy == '('
+ pos = s.position()
+ s.next()
+ positional_args = []
+ keyword_args = []
+ star_arg = None
+ starstar_arg = None
+ while s.sy not in ('*', '**', ')'):
+ arg = p_simple_expr(s)
+ if s.sy == '=':
+ s.next()
+ if not arg.is_name:
+ s.error("Expected an identifier before '='",
+ pos = arg.pos)
+ keyword = ExprNodes.StringNode(arg.pos,
+ value = arg.name)
+ arg = p_simple_expr(s)
+ keyword_args.append((keyword, arg))
+ else:
+ if keyword_args:
+ s.error("Non-keyword arg following keyword arg",
+ pos = arg.pos)
+ positional_args.append(arg)
+ if s.sy <> ',':
+ break
+ s.next()
+ if s.sy == '*':
+ s.next()
+ star_arg = p_simple_expr(s)
+ if s.sy == ',':
+ s.next()
+ if s.sy == '**':
+ s.next()
+ starstar_arg = p_simple_expr(s)
+ if s.sy == ',':
+ s.next()
+ s.expect(')')
+ if not (keyword_args or star_arg or starstar_arg):
+ return ExprNodes.SimpleCallNode(pos,
+ function = function,
+ args = positional_args)
+ else:
+ arg_tuple = None
+ keyword_dict = None
+ if positional_args or not star_arg:
+ arg_tuple = ExprNodes.TupleNode(pos,
+ args = positional_args)
+ if star_arg:
+ star_arg_tuple = ExprNodes.AsTupleNode(pos, arg = star_arg)
+ if arg_tuple:
+ arg_tuple = ExprNodes.binop_node(pos,
+ operator = '+', operand1 = arg_tuple,
+ operand2 = star_arg_tuple)
+ else:
+ arg_tuple = star_arg_tuple
+ if keyword_args:
+ keyword_dict = ExprNodes.DictNode(pos,
+ key_value_pairs = keyword_args)
+ return ExprNodes.GeneralCallNode(pos,
+ function = function,
+ positional_args = arg_tuple,
+ keyword_args = keyword_dict,
+ starstar_arg = starstar_arg)
+
+#lambdef: 'lambda' [varargslist] ':' test
+
+#subscriptlist: subscript (',' subscript)* [',']
+
+def p_index(s, base):
+ # s.sy == '['
+ pos = s.position()
+ s.next()
+ subscripts = p_subscript_list(s)
+ if len(subscripts) == 1 and len(subscripts[0]) == 2:
+ start, stop = subscripts[0]
+ result = ExprNodes.SliceIndexNode(pos,
+ base = base, start = start, stop = stop)
+ else:
+ indexes = make_slice_nodes(pos, subscripts)
+ if len(indexes) == 1:
+ index = indexes[0]
+ else:
+ index = ExprNodes.TupleNode(pos, args = indexes)
+ result = ExprNodes.IndexNode(pos,
+ base = base, index = index)
+ s.expect(']')
+ return result
+
+def p_subscript_list(s):
+ items = [p_subscript(s)]
+ while s.sy == ',':
+ s.next()
+ if s.sy == ']':
+ break
+ items.append(p_subscript(s))
+ return items
+
+#subscript: '.' '.' '.' | test | [test] ':' [test] [':' [test]]
+
+def p_subscript(s):
+ # Parse a subscript and return a list of
+ # 1, 2 or 3 ExprNodes, depending on how
+ # many slice elements were encountered.
+ pos = s.position()
+ if s.sy == '.':
+ expect_ellipsis(s)
+ return [ExprNodes.EllipsisNode(pos)]
+ else:
+ start = p_slice_element(s, (':',))
+ if s.sy <> ':':
+ return [start]
+ s.next()
+ stop = p_slice_element(s, (':', ',', ']'))
+ if s.sy <> ':':
+ return [start, stop]
+ s.next()
+ step = p_slice_element(s, (':', ',', ']'))
+ return [start, stop, step]
+
+def p_slice_element(s, follow_set):
+ # Simple expression which may be missing iff
+ # it is followed by something in follow_set.
+ if s.sy not in follow_set:
+ return p_simple_expr(s)
+ else:
+ return None
+
+def expect_ellipsis(s):
+ s.expect('.')
+ s.expect('.')
+ s.expect('.')
+
+def make_slice_nodes(pos, subscripts):
+ # Convert a list of subscripts as returned
+ # by p_subscript_list into a list of ExprNodes,
+ # creating SliceNodes for elements with 2 or
+ # more components.
+ result = []
+ for subscript in subscripts:
+ if len(subscript) == 1:
+ result.append(subscript[0])
+ else:
+ result.append(make_slice_node(pos, *subscript))
+ return result
+
+def make_slice_node(pos, start, stop = None, step = None):
+ if not start:
+ start = ExprNodes.NoneNode(pos)
+ if not stop:
+ stop = ExprNodes.NoneNode(pos)
+ if not step:
+ step = ExprNodes.NoneNode(pos)
+ return ExprNodes.SliceNode(pos,
+ start = start, stop = stop, step = step)
+
+#atom: '(' [testlist] ')' | '[' [listmaker] ']' | '{' [dictmaker] '}' | '`' testlist '`' | NAME | NUMBER | STRING+
+
+def p_atom(s):
+ pos = s.position()
+ sy = s.sy
+ if sy == '(':
+ s.next()
+ if s.sy == ')':
+ result = ExprNodes.TupleNode(pos, args = [])
+ else:
+ result = p_expr(s)
+ s.expect(')')
+ return result
+ elif sy == '[':
+ return p_list_maker(s)
+ elif sy == '{':
+ return p_dict_maker(s)
+ elif sy == '`':
+ return p_backquote_expr(s)
+ elif sy == 'INT':
+ value = s.systring
+ s.next()
+ return ExprNodes.IntNode(pos, value = value)
+ elif sy == 'LONG':
+ value = s.systring
+ s.next()
+ return ExprNodes.LongNode(pos, value = value)
+ elif sy == 'FLOAT':
+ value = s.systring
+ s.next()
+ return ExprNodes.FloatNode(pos, value = value)
+ elif sy == 'IMAG':
+ value = s.systring[:-1]
+ s.next()
+ return ExprNodes.ImagNode(pos, value = value)
+ elif sy == 'STRING' or sy == 'BEGIN_STRING':
+ kind, value = p_cat_string_literal(s)
+ if kind == 'c':
+ return ExprNodes.CharNode(pos, value = value)
+ else:
+ return ExprNodes.StringNode(pos, value = value)
+ elif sy == 'IDENT':
+ name = s.systring
+ s.next()
+ if name == "None":
+ return ExprNodes.NoneNode(pos)
+ elif name == "new" and s.sy == 'IDENT':
+ return p_new_call(s)
+ else:
+ return p_name_atom(s, name)
+ elif sy == 'NULL':
+ s.next()
+ return ExprNodes.NullNode(pos)
+ else:
+ s.error("Expected an identifier or literal")
+
+def p_new_call(s):
+ node = p_primitive(s)
+ if isinstance(node, ExprNodes.SimpleCallNode):
+ node.is_new = 1
+ else:
+ error(s.position(), "'new' must be followed by a C++ constructor call")
+ return node
+
+def p_name(s):
+ if s.sy == 'IDENT':
+ pos = s.position()
+ name = s.systring
+ s.next()
+ return ExprNodes.NameNode(pos, name = name)
+ else:
+ s.error("Expected a variable name")
+
+def p_name_atom(s, name):
+ pos = s.position()
+ if not s.compile_time_expr:
+ try:
+ value = s.compile_time_env.lookup_here(name)
+ except KeyError:
+ pass
+ else:
+ rep = repr(value)
+ if isinstance(value, int):
+ return ExprNodes.IntNode(pos, value = rep)
+ elif isinstance(value, long):
+ return ExprNodes.LongNode(pos, value = rep)
+ elif isinstance(value, float):
+ return ExprNodes.FloatNode(pos, value = rep)
+ elif isinstance(value, str):
+ return ExprNodes.StringNode(pos, value = rep[1:-1])
+ else:
+ error(pos, "Invalid type for compile-time constant: %s"
+ % value.__class__.__name__)
+ return ExprNodes.NameNode(pos, name = name)
+
+def p_cat_string_literal(s):
+ # A sequence of one or more adjacent string literals.
+ # Returns (kind, value) where kind in ('', 'c', 'r')
+ kind, value = p_string_literal(s)
+ if kind <> 'c':
+ strings = [value]
+ while s.sy == 'STRING' or s.sy == 'BEGIN_STRING':
+ next_kind, next_value = p_string_literal(s)
+ if next_kind == 'c':
+ self.error(
+ "Cannot concatenate char literal with another string or char literal")
+ strings.append(next_value)
+ value = ''.join(strings)
+ return kind, value
+
+def p_opt_string_literal(s):
+ if s.sy == 'STRING' or s.sy == 'BEGIN_STRING':
+ return p_string_literal(s)
+ else:
+ return None
+
+def p_string_literal(s):
+ # A single string or char literal.
+ # Returns (kind, value) where kind in ('', 'c', 'r')
+ if s.sy == 'STRING':
+ value = unquote(s.systring)
+ s.next()
+ return value
+ # s.sy == 'BEGIN_STRING'
+ pos = s.position()
+ #is_raw = s.systring[:1].lower() == "r"
+ kind = s.systring[:1].lower()
+ if kind not in "cr":
+ kind = ''
+ chars = []
+ while 1:
+ s.next()
+ sy = s.sy
+ #print "p_string_literal: sy =", sy, repr(s.systring) ###
+ if sy == 'CHARS':
+ systr = s.systring
+ if len(systr) == 1 and systr in "'\"\n":
+ chars.append('\\')
+ chars.append(systr)
+ elif sy == 'ESCAPE':
+ systr = s.systring
+ if kind == 'r':
+ if systr == '\\\n':
+ chars.append(r'\\\n')
+ elif systr == r'\"':
+ chars.append(r'\\\"')
+ elif systr == r'\\':
+ chars.append(r'\\\\')
+ else:
+ chars.append('\\' + systr)
+ else:
+ c = systr[1]
+ if c in "'\"\\abfnrtv01234567":
+ chars.append(systr)
+ elif c == 'x':
+ chars.append('\\x0' + systr[2:])
+ elif c == '\n':
+ pass
+ else:
+ chars.append(r'\\' + systr[1:])
+ elif sy == 'NEWLINE':
+ chars.append(r'\n')
+ elif sy == 'END_STRING':
+ break
+ elif sy == 'EOF':
+ s.error("Unclosed string literal", pos = pos)
+ else:
+ s.error(
+ "Unexpected token %r:%r in string literal" %
+ (sy, s.systring))
+ s.next()
+ value = join(chars, '')
+ #print "p_string_literal: value =", repr(value) ###
+ return kind, value
+
+def unquote(s):
+ is_raw = 0
+ if s[:1].lower() == "r":
+ is_raw = 1
+ s = s[1:]
+ q = s[:3]
+ if q == '"""' or q == "'''":
+ s = s[3:-3]
+ else:
+ s = s[1:-1]
+ if is_raw:
+ s = s.replace('\\', '\\\\')
+ s = s.replace('\n', '\\\n')
+ else:
+ # Split into double quotes, newlines, escape sequences
+ # and spans of regular chars
+ l1 = re.split(r'((?:\\[0-7]{1,3})|(?:\\x[0-9A-Fa-f]{2})|(?:\\.)|(?:\\\n)|(?:\n)|")', s)
+ print "unquote: l1 =", l1 ###
+ l2 = []
+ for item in l1:
+ if item == '"' or item == '\n':
+ l2.append('\\' + item)
+ elif item == '\\\n':
+ pass
+ elif item[:1] == '\\':
+ if len(item) == 2:
+ if item[1] in '"\\abfnrtv':
+ l2.append(item)
+ else:
+ l2.append(item[1])
+ elif item[1:2] == 'x':
+ l2.append('\\x0' + item[2:])
+ else:
+ # octal escape
+ l2.append(item)
+ else:
+ l2.append(item)
+ s = "".join(l2)
+ return s
+
+def p_list_maker(s):
+ # s.sy == '['
+ pos = s.position()
+ s.next()
+ exprs = p_simple_expr_list(s)
+ s.expect(']')
+ return ExprNodes.ListNode(pos, args = exprs)
+
+#dictmaker: test ':' test (',' test ':' test)* [',']
+
+def p_dict_maker(s):
+ # s.sy == '{'
+ pos = s.position()
+ s.next()
+ items = []
+ while s.sy <> '}':
+ key = p_simple_expr(s)
+ s.expect(':')
+ value = p_simple_expr(s)
+ items.append((key, value))
+ if s.sy <> ',':
+ break
+ s.next()
+ s.expect('}')
+ return ExprNodes.DictNode(pos, key_value_pairs = items)
+
+def p_backquote_expr(s):
+ # s.sy == '`'
+ pos = s.position()
+ s.next()
+ arg = p_expr(s)
+ s.expect('`')
+ return ExprNodes.BackquoteNode(pos, arg = arg)
+
+#testlist: test (',' test)* [',']
+
+def p_simple_expr_list(s):
+ exprs = []
+ while s.sy not in expr_terminators:
+ exprs.append(p_simple_expr(s))
+ if s.sy <> ',':
+ break
+ s.next()
+ return exprs
+
+def p_expr(s):
+ pos = s.position()
+ expr = p_simple_expr(s)
+ if s.sy == ',':
+ s.next()
+ exprs = [expr] + p_simple_expr_list(s)
+ return ExprNodes.TupleNode(pos, args = exprs)
+ else:
+ return expr
+
+expr_terminators = (')', ']', '}', ':', '=', 'NEWLINE')
+
+#-------------------------------------------------------
+#
+# Statements
+#
+#-------------------------------------------------------
+
+def p_global_statement(s):
+ # assume s.sy == 'global'
+ pos = s.position()
+ s.next()
+ names = p_ident_list(s)
+ return Nodes.GlobalNode(pos, names = names)
+
+inplace_operators = ('+=', '-=', '*=', '/=', '%=', '**=',
+ '<<=', '>>=', '&=', '^=', '|=')
+
+def p_expression_or_assignment(s):
+ pos = s.position()
+ expr = p_expr(s)
+ if s.sy in inplace_operators:
+ return p_inplace_operation(s, expr)
+ elif s.sy <> '=':
+ if isinstance(expr, ExprNodes.StringNode):
+ return Nodes.PassStatNode(expr.pos)
+ else:
+ return Nodes.ExprStatNode(expr.pos, expr = expr)
+ else:
+ expr_list = [expr]
+ while s.sy == '=':
+ s.next()
+ expr_list.append(p_expr(s))
+ expr_list_list = []
+ flatten_parallel_assignments(expr_list, expr_list_list)
+ nodes = []
+ for expr_list in expr_list_list:
+ lhs_list = expr_list[:-1]
+ rhs = expr_list[-1]
+ if len(lhs_list) == 1:
+ node = Nodes.SingleAssignmentNode(rhs.pos,
+ lhs = lhs_list[0], rhs = rhs)
+ else:
+ node = Nodes.CascadedAssignmentNode(rhs.pos,
+ lhs_list = lhs_list, rhs = rhs)
+ nodes.append(node)
+ if len(nodes) == 1:
+ return nodes[0]
+ else:
+ return Nodes.ParallelAssignmentNode(nodes[0].pos, stats = nodes)
+
+def p_inplace_operation(s, lhs):
+ pos = s.position()
+ op = s.sy
+ s.next()
+ rhs = p_expr(s)
+ return Nodes.AugmentedAssignmentNode(pos, lhs = lhs, operator = op, rhs = rhs)
+
+def flatten_parallel_assignments(input, output):
+ # The input is a list of expression nodes, representing
+ # the LHSs and RHS of one (possibly cascaded) assignment
+ # statement. If they are all sequence constructors with
+ # the same number of arguments, rearranges them into a
+ # list of equivalent assignments between the individual
+ # elements. This transformation is applied recursively.
+ size = find_parallel_assignment_size(input)
+ if size >= 0:
+ for i in range(size):
+ new_exprs = [expr.args[i] for expr in input]
+ flatten_parallel_assignments(new_exprs, output)
+ else:
+ output.append(input)
+
+def find_parallel_assignment_size(input):
+ # The input is a list of expression nodes. If
+ # they are all sequence constructors with the same number
+ # of arguments, return that number, else return -1.
+ # Produces an error message if they are all sequence
+ # constructors but not all the same size.
+ for expr in input:
+ if not expr.is_sequence_constructor:
+ return -1
+ rhs = input[-1]
+ rhs_size = len(rhs.args)
+ for lhs in input[:-1]:
+ lhs_size = len(lhs.args)
+ if lhs_size <> rhs_size:
+ error(lhs.pos, "Unpacking sequence of wrong size (expected %d, got %d)"
+ % (lhs_size, rhs_size))
+ return -1
+ return rhs_size
+
+def p_print_statement(s):
+ # s.sy == 'print'
+ pos = s.position()
+ s.next()
+ if s.sy == '>>':
+ s.error("'print >>' not yet implemented")
+ args = []
+ ewc = 0
+ if s.sy not in ('NEWLINE', 'EOF'):
+ args.append(p_simple_expr(s))
+ while s.sy == ',':
+ s.next()
+ if s.sy in ('NEWLINE', 'EOF'):
+ ewc = 1
+ break
+ args.append(p_simple_expr(s))
+ return Nodes.PrintStatNode(pos,
+ args = args, ends_with_comma = ewc)
+
+def p_del_statement(s):
+ # s.sy == 'del'
+ pos = s.position()
+ s.next()
+ args = p_simple_expr_list(s)
+ return Nodes.DelStatNode(pos, args = args)
+
+def p_pass_statement(s, with_newline = 0):
+ pos = s.position()
+ s.expect('pass')
+ if with_newline:
+ s.expect_newline("Expected a newline")
+ return Nodes.PassStatNode(pos)
+
+def p_break_statement(s):
+ # s.sy == 'break'
+ pos = s.position()
+ s.next()
+ return Nodes.BreakStatNode(pos)
+
+def p_continue_statement(s):
+ # s.sy == 'continue'
+ pos = s.position()
+ s.next()
+ return Nodes.ContinueStatNode(pos)
+
+def p_return_statement(s):
+ # s.sy == 'return'
+ pos = s.position()
+ s.next()
+ if s.sy not in statement_terminators:
+ value = p_expr(s)
+ else:
+ value = None
+ return Nodes.ReturnStatNode(pos, value = value)
+
+def p_raise_statement(s):
+ # s.sy == 'raise'
+ pos = s.position()
+ s.next()
+ exc_type = None
+ exc_value = None
+ exc_tb = None
+ if s.sy not in statement_terminators:
+ exc_type = p_simple_expr(s)
+ if s.sy == ',':
+ s.next()
+ exc_value = p_simple_expr(s)
+ if s.sy == ',':
+ s.next()
+ exc_tb = p_simple_expr(s)
+ if exc_type or exc_value or exc_tb:
+ return Nodes.RaiseStatNode(pos,
+ exc_type = exc_type,
+ exc_value = exc_value,
+ exc_tb = exc_tb)
+ else:
+ return Nodes.ReraiseStatNode(pos)
+
+def p_import_statement(s):
+ # s.sy in ('import', 'cimport')
+ pos = s.position()
+ kind = s.sy
+ s.next()
+ items = [p_dotted_name(s, as_allowed = 1)]
+ while s.sy == ',':
+ s.next()
+ items.append(p_dotted_name(s, as_allowed = 1))
+ stats = []
+ for pos, target_name, dotted_name, as_name in items:
+ if kind == 'cimport':
+ stat = Nodes.CImportStatNode(pos,
+ module_name = dotted_name,
+ as_name = as_name)
+ else:
+ if as_name and "." in dotted_name:
+ name_list = ExprNodes.ListNode(pos, args = [
+ ExprNodes.StringNode(pos, value = "*")])
+ else:
+ name_list = None
+ stat = Nodes.SingleAssignmentNode(pos,
+ lhs = ExprNodes.NameNode(pos,
+ name = as_name or target_name),
+ rhs = ExprNodes.ImportNode(pos,
+ module_name = ExprNodes.StringNode(pos,
+ value = dotted_name),
+ name_list = name_list))
+ stats.append(stat)
+ return Nodes.StatListNode(pos, stats = stats)
+
+def p_from_import_statement(s, ctx):
+ # s.sy == 'from'
+ pos = s.position()
+ s.next()
+ (dotted_name_pos, _, dotted_name, _) = \
+ p_dotted_name(s, as_allowed = 0)
+ if s.sy in ('import', 'cimport'):
+ kind = s.sy
+ s.next()
+ else:
+ s.error("Expected 'import' or 'cimport'")
+ if kind == 'cimport' and ctx.level not in ('module', 'module_pxd'):
+ s.error("cimport statement not allowed in this context")
+ if s.sy == '*':
+ s.error("'import *' not supported")
+ is_cimport = kind == 'cimport'
+ imported_names = [p_imported_name(s, is_cimport)]
+ while s.sy == ',':
+ s.next()
+ imported_names.append(p_imported_name(s, is_cimport))
+ if kind == 'cimport':
+ for imp in imported_names:
+ local_name = imp.as_name or imp.name
+ s.add_type_name(local_name)
+ return Nodes.FromCImportStatNode(pos,
+ module_name = dotted_name,
+ imported_names = imported_names)
+ else:
+ imported_name_strings = []
+ items = []
+ for imp in imported_names:
+ imported_name_strings.append(
+ ExprNodes.StringNode(imp.pos, value = imp.name))
+ items.append(
+ (imp.name,
+ ExprNodes.NameNode(imp.pos,
+ name = imp.as_name or imp.name)))
+ import_list = ExprNodes.ListNode(
+ imported_names[0].pos, args = imported_name_strings)
+ return Nodes.FromImportStatNode(pos,
+ module = ExprNodes.ImportNode(dotted_name_pos,
+ module_name = ExprNodes.StringNode(dotted_name_pos,
+ value = dotted_name),
+ name_list = import_list),
+ items = items)
+
+class ImportedName(object):
+ # pos
+ # name
+ # as_name
+ # kind 'class', 'struct', 'union', None
+
+ def __init__(self, pos, name, as_name, kind):
+ self.pos = pos
+ self.name = name
+ self.as_name = as_name
+ self.kind = kind
+
+imported_name_kinds = ('class', 'struct', 'union')
+
+def p_imported_name(s, is_cimport):
+ pos = s.position()
+ kind = None
+ if is_cimport and s.systring in imported_name_kinds:
+ kind = s.systring
+ s.next()
+ name = p_ident(s)
+ as_name = p_as_name(s)
+ return ImportedName(pos, name, as_name, kind)
+
+def p_dotted_name(s, as_allowed):
+ pos = s.position()
+ target_name = p_ident(s)
+ as_name = None
+ names = [target_name]
+ while s.sy == '.':
+ s.next()
+ names.append(p_ident(s))
+ if as_allowed:
+ as_name = p_as_name(s)
+ return (pos, target_name, join(names, "."), as_name)
+
+def p_as_name(s):
+ if s.sy == 'IDENT' and s.systring == 'as':
+ s.next()
+ return p_ident(s)
+ else:
+ return None
+
+def p_assert_statement(s):
+ # s.sy == 'assert'
+ pos = s.position()
+ s.next()
+ cond = p_simple_expr(s)
+ if s.sy == ',':
+ s.next()
+ value = p_simple_expr(s)
+ else:
+ value = None
+ return Nodes.AssertStatNode(pos, cond = cond, value = value)
+
+statement_terminators = (';', 'NEWLINE', 'EOF')
+
+def p_if_statement(s):
+ # s.sy == 'if'
+ pos = s.position()
+ s.next()
+ if_clauses = [p_if_clause(s)]
+ while s.sy == 'elif':
+ s.next()
+ if_clauses.append(p_if_clause(s))
+ else_clause = p_else_clause(s)
+ return Nodes.IfStatNode(pos,
+ if_clauses = if_clauses, else_clause = else_clause)
+
+def p_if_clause(s):
+ pos = s.position()
+ test = p_simple_expr(s)
+ body = p_suite(s)
+ return Nodes.IfClauseNode(pos,
+ condition = test, body = body)
+
+def p_else_clause(s):
+ if s.sy == 'else':
+ s.next()
+ return p_suite(s)
+ else:
+ return None
+
+def p_while_statement(s):
+ # s.sy == 'while'
+ pos = s.position()
+ s.next()
+ test = p_simple_expr(s)
+ body = p_suite(s)
+ else_clause = p_else_clause(s)
+ return Nodes.WhileStatNode(pos,
+ condition = test, body = body,
+ else_clause = else_clause)
+
+def p_for_statement(s):
+ # s.sy == 'for'
+ pos = s.position()
+ s.next()
+ expr = p_for_expr(s)
+ if s.sy == 'in':
+ return p_standard_for_statement(s, expr)
+ elif s.sy in inequality_relations:
+ return p_integer_for_statement(s, expr)
+ elif s.sy == 'from':
+ #warning(pos, "Old-style integer for-loop is deprecated, use 'for x < i < y' instead")
+ return p_old_style_integer_for_statement(s, expr)
+ else:
+ s.error("Expected 'in' or an inequality relation")
+
+def p_standard_for_statement(s, target):
+ # s.sy == 'in'
+ s.next()
+ iterator = p_for_iterator(s)
+ body = p_suite(s)
+ else_clause = p_else_clause(s)
+ return Nodes.ForInStatNode(target.pos,
+ target = target,
+ iterator = iterator,
+ body = body,
+ else_clause = else_clause)
+
+def p_integer_for_statement(s, bound1):
+ rel1 = s.sy
+ s.next()
+ name_pos = s.position()
+ target = p_name(s)
+ rel2_pos = s.position()
+ rel2 = p_inequality_relation(s)
+ bound2 = p_bit_expr(s)
+ if rel1[0] <> rel2[0]:
+ error(rel2_pos,
+ "Relation directions in integer for-loop do not match")
+ body = p_suite(s)
+ else_clause = p_else_clause(s)
+ return Nodes.IntegerForStatNode(bound1.pos,
+ bound1 = bound1,
+ relation1 = rel1,
+ target = target,
+ relation2 = rel2,
+ bound2 = bound2,
+ body = body,
+ else_clause = else_clause)
+
+def p_old_style_integer_for_statement(s, target):
+ # s.sy == 'for'
+ s.next()
+ bound1 = p_bit_expr(s)
+ rel1 = p_inequality_relation(s)
+ name2_pos = s.position()
+ name2 = p_ident(s)
+ rel2_pos = s.position()
+ rel2 = p_inequality_relation(s)
+ bound2 = p_bit_expr(s)
+ if not target.is_name:
+ error(target.pos,
+ "Target of for-from statement must be a variable name")
+ elif name2 <> target.name:
+ error(name2_pos,
+ "Variable name in for-from range does not match target")
+ if rel1[0] <> rel2[0]:
+ error(rel2_pos,
+ "Relation directions in for-from do not match")
+ body = p_suite(s)
+ else_clause = p_else_clause(s)
+ return Nodes.IntegerForStatNode(bound1.pos,
+ bound1 = bound1,
+ relation1 = rel1,
+ target = target,
+ relation2 = rel2,
+ bound2 = bound2,
+ body = body,
+ else_clause = else_clause)
+
+def p_inequality_relation(s):
+ if s.sy in inequality_relations:
+ op = s.sy
+ s.next()
+ return op
+ else:
+ s.error("Expected one of '<', '<=', '>' '>='")
+
+inequality_relations = ('<', '<=', '>', '>=')
+
+def p_for_expr(s):
+ # Target of standard for-statement or first bound of integer for-statement
+ pos = s.position()
+ expr = p_bit_expr(s)
+ if s.sy == ',':
+ s.next()
+ exprs = [expr]
+ while s.sy <> 'in':
+ exprs.append(p_bit_expr(s))
+ if s.sy <> ',':
+ break
+ s.next()
+ return ExprNodes.TupleNode(pos, args = exprs)
+ else:
+ return expr
+
+def p_for_iterator(s):
+ pos = s.position()
+ expr = p_expr(s)
+ return ExprNodes.IteratorNode(pos, sequence = expr)
+
+def p_try_statement(s):
+ # s.sy == 'try'
+ pos = s.position()
+ s.next()
+ body = p_suite(s)
+ except_clauses = []
+ else_clause = None
+ if s.sy in ('except', 'else'):
+ while s.sy == 'except':
+ except_clauses.append(p_except_clause(s))
+ if s.sy == 'else':
+ s.next()
+ else_clause = p_suite(s)
+ return Nodes.TryExceptStatNode(pos,
+ body = body, except_clauses = except_clauses,
+ else_clause = else_clause)
+ elif s.sy == 'finally':
+ s.next()
+ finally_clause = p_suite(s)
+ return Nodes.TryFinallyStatNode(pos,
+ body = body, finally_clause = finally_clause)
+ else:
+ s.error("Expected 'except' or 'finally'")
+
+def p_except_clause(s):
+ # s.sy == 'except'
+ pos = s.position()
+ s.next()
+ exc_type = None
+ exc_value = None
+ tb_value = None
+ if s.sy <> ':':
+ exc_type = p_simple_expr(s)
+ if s.sy == ',':
+ s.next()
+ exc_value = p_simple_expr(s)
+ if s.sy == ',':
+ s.next()
+ tb_value = p_simple_expr(s)
+ body = p_suite(s)
+ return Nodes.ExceptClauseNode(pos,
+ pattern = exc_type, exc_target = exc_value, tb_target = tb_value, body = body)
+
+def p_include_statement(s, ctx):
+ pos = s.position()
+ s.next() # 'include'
+ _, include_file_name = p_string_literal(s)
+ s.expect_newline("Syntax error in include statement")
+ if s.compile_time_eval:
+ include_file_path = s.context.find_include_file(include_file_name, pos)
+ if include_file_path:
+ s.included_files.append(include_file_name)
+ f = open(include_file_path, "rU")
+ s2 = PyrexScanner(f, include_file_path, parent_scanner = s)
+ try:
+ tree = p_statement_list(s2, ctx)
+ finally:
+ f.close()
+ return tree
+ else:
+ return None
+ else:
+ return Nodes.PassStatNode(pos)
+
+def p_with_statement(s):
+ pos = s.position()
+ s.next() # 'with'
+# if s.sy == 'IDENT' and s.systring in ('gil', 'nogil'):
+ if s.sy == 'IDENT' and s.systring == 'nogil':
+ state = s.systring
+ s.next()
+ body = p_suite(s)
+ return Nodes.GILStatNode(pos, state = state, body = body)
+ else:
+ s.error("Only 'with nogil' implemented")
+
+def p_simple_statement(s, ctx):
+ if s.sy == 'global':
+ node = p_global_statement(s)
+ elif s.sy == 'print':
+ node = p_print_statement(s)
+ elif s.sy == 'del':
+ node = p_del_statement(s)
+ elif s.sy == 'break':
+ node = p_break_statement(s)
+ elif s.sy == 'continue':
+ node = p_continue_statement(s)
+ elif s.sy == 'return':
+ node = p_return_statement(s)
+ elif s.sy == 'raise':
+ node = p_raise_statement(s)
+ elif s.sy == 'cimport':
+ if ctx.level not in ('module', 'module_pxd'):
+ s.error("cimport statement not allowed in this context")
+ node = p_import_statement(s)
+ elif s.sy == 'import':
+ node = p_import_statement(s)
+ elif s.sy == 'from':
+ node = p_from_import_statement(s, ctx)
+ elif s.sy == 'assert':
+ node = p_assert_statement(s)
+ elif s.sy == 'pass':
+ node = p_pass_statement(s)
+ else:
+ node = p_expression_or_assignment(s)
+ return node
+
+def p_simple_statement_list(s, ctx):
+ # Parse a series of simple statements on one line
+ # separated by semicolons.
+ stat = p_simple_statement(s, ctx)
+ if s.sy == ';':
+ stats = [stat]
+ while s.sy == ';':
+ s.next()
+ if s.sy in ('NEWLINE', 'EOF'):
+ break
+ stats.append(p_simple_statement(s, ctx))
+ stat = Nodes.StatListNode(stats[0].pos, stats = stats)
+ s.expect_newline("Syntax error in simple statement list")
+ return stat
+
+def p_compile_time_expr(s):
+ old = s.compile_time_expr
+ s.compile_time_expr = 1
+ expr = p_expr(s)
+ s.compile_time_expr = old
+ return expr
+
+def p_DEF_statement(s):
+ pos = s.position()
+ denv = s.compile_time_env
+ s.next() # 'DEF'
+ name = p_ident(s)
+ s.expect('=')
+ expr = p_compile_time_expr(s)
+ value = expr.compile_time_value(denv)
+ #print "p_DEF_statement: %s = %r" % (name, value) ###
+ denv.declare(name, value)
+ s.expect_newline()
+ return Nodes.PassStatNode(pos)
+
+def p_IF_statement(s, ctx):
+ pos = s.position()
+ saved_eval = s.compile_time_eval
+ current_eval = saved_eval
+ denv = s.compile_time_env
+ result = None
+ while 1:
+ s.next() # 'IF' or 'ELIF'
+ expr = p_compile_time_expr(s)
+ s.compile_time_eval = current_eval and bool(expr.compile_time_value(denv))
+ body = p_suite(s, ctx)
+ if s.compile_time_eval:
+ result = body
+ current_eval = 0
+ if s.sy <> 'ELIF':
+ break
+ if s.sy == 'ELSE':
+ s.next()
+ s.compile_time_eval = current_eval
+ body = p_suite(s, ctx)
+ if current_eval:
+ result = body
+ if not result:
+ result = Nodes.PassStatNode(pos)
+ s.compile_time_eval = saved_eval
+ return result
+
+def p_statement(s, ctx):
+ pos = s.position()
+ cdef_flag = ctx.cdef_flag
+ if s.sy == 'ctypedef':
+ if ctx.level not in ('module', 'module_pxd'):
+ s.error("ctypedef statement not allowed here")
+ #if ctx.api:
+ # error(s.pos, "'api' not allowed with 'ctypedef'")
+ return p_ctypedef_statement(s, ctx)
+ elif s.sy == 'DEF':
+ return p_DEF_statement(s)
+ elif s.sy == 'IF':
+ return p_IF_statement(s, ctx)
+ else:
+ if s.sy == 'cdef':
+ cdef_flag = 1
+ s.next()
+ if s.sy == '+':
+ ctx = ctx(cplus_flag = 1)
+ s.next()
+ if cdef_flag:
+ if ctx.level not in ('module', 'module_pxd', 'function', 'c_class', 'c_class_pxd'):
+ s.error('cdef statement not allowed here')
+ return p_cdef_statement(s, ctx)
+ else:
+ if ctx.api:
+ error(s.pos, "'api' not allowed with this statement")
+ if s.sy == 'def':
+ if ctx.level not in ('module', 'class', 'c_class', 'property'):
+ s.error('def statement not allowed here')
+ return p_def_statement(s)
+ elif s.sy == 'class':
+ if ctx.level <> 'module':
+ s.error("class definition not allowed here")
+ return p_class_statement(s)
+ elif s.sy == 'include':
+ #if ctx.level not in ('module', 'module_pxd'):
+ # s.error("include statement not allowed here")
+ return p_include_statement(s, ctx)
+ elif ctx.level == 'c_class' and s.sy == 'IDENT' and s.systring == 'property':
+ return p_property_decl(s)
+ elif s.sy == 'pass' and ctx.level <> 'property':
+ return p_pass_statement(s, with_newline = 1)
+ else:
+ if ctx.level in ('c_class', 'c_class_pxd', 'property'):
+ s.error("Executable statement not allowed here")
+ if s.sy == 'if':
+ return p_if_statement(s)
+ elif s.sy == 'while':
+ return p_while_statement(s)
+ elif s.sy == 'for':
+ return p_for_statement(s)
+ elif s.sy == 'try':
+ return p_try_statement(s)
+ elif s.sy == 'with':
+ return p_with_statement(s)
+ else:
+ return p_simple_statement_list(s, ctx)
+
+def p_statement_list(s, ctx):
+ # Parse a series of statements separated by newlines.
+ pos = s.position()
+ stats = []
+ while s.sy not in ('DEDENT', 'EOF'):
+ stats.append(p_statement(s, ctx))
+ if len(stats) == 1:
+ return stats[0]
+ else:
+ return Nodes.StatListNode(pos, stats = stats)
+
+def p_suite(s, ctx = Ctx(), with_doc = 0, with_pseudo_doc = 0):
+ pos = s.position()
+ s.expect(':')
+ doc = None
+ stmts = []
+ if s.sy == 'NEWLINE':
+ s.next()
+ s.expect_indent()
+ if with_doc or with_pseudo_doc:
+ doc = p_doc_string(s)
+ body = p_statement_list(s, ctx)
+ s.expect_dedent()
+ else:
+ if ctx.api:
+ error(s.pos, "'api' not allowed with this statement")
+ if ctx.level in ('module', 'class', 'function', 'other'):
+ body = p_simple_statement_list(s, ctx)
+ else:
+ body = p_pass_statement(s)
+ s.expect_newline("Syntax error in declarations")
+ if with_doc:
+ return doc, body
+ else:
+ return body
+
+def p_c_base_type(s, self_flag = 0):
+ # If self_flag is true, this is the base type for the
+ # self argument of a C method of an extension type.
+ if s.sy == '(':
+ return p_c_complex_base_type(s)
+ else:
+ return p_c_simple_base_type(s, self_flag)
+
+def p_calling_convention(s):
+ if s.sy == 'IDENT' and s.systring in calling_convention_words:
+ result = s.systring
+ s.next()
+ return result
+ else:
+ return ""
+
+calling_convention_words = ("__stdcall", "__cdecl", "__fastcall")
+
+def p_c_complex_base_type(s):
+ # s.sy == '('
+ pos = s.position()
+ s.next()
+ base_type = p_c_base_type(s)
+ declarator = p_c_declarator(s, empty = 1)
+ s.expect(')')
+ return Nodes.CComplexBaseTypeNode(pos,
+ base_type = base_type, declarator = declarator)
+
+def p_c_simple_base_type(s, self_flag):
+ #print "p_c_simple_base_type: self_flag =", self_flag
+ is_basic = 0
+ signed = 1
+ longness = 0
+ module_path = []
+ pos = s.position()
+ if looking_at_base_type(s):
+ #print "p_c_simple_base_type: looking_at_base_type at", s.position()
+ is_basic = 1
+ signed, longness = p_sign_and_longness(s)
+ if s.sy == 'IDENT' and s.systring in basic_c_type_names:
+ name = s.systring
+ s.next()
+ else:
+ name = 'int'
+ elif s.looking_at_type_name() or looking_at_dotted_name(s):
+ #print "p_c_simple_base_type: looking_at_type_name at", s.position()
+ name = s.systring
+ s.next()
+ while s.sy == '.':
+ module_path.append(name)
+ s.next()
+ name = p_ident(s)
+ else:
+ #print "p_c_simple_base_type: not looking at type at", s.position()
+ name = None
+ return Nodes.CSimpleBaseTypeNode(pos,
+ name = name, module_path = module_path,
+ is_basic_c_type = is_basic, signed = signed,
+ longness = longness, is_self_arg = self_flag)
+
+def looking_at_type(s):
+ return looking_at_base_type(s) or s.looking_at_type_name()
+
+def looking_at_base_type(s):
+ #print "looking_at_base_type?", s.sy, s.systring, s.position()
+ return s.sy == 'IDENT' and s.systring in base_type_start_words
+
+def looking_at_dotted_name(s):
+ if s.sy == 'IDENT':
+ name = s.systring
+ s.next()
+ result = s.sy == '.'
+ s.put_back('IDENT', name)
+ return result
+ else:
+ return 0
+
+basic_c_type_names = ("void", "char", "int", "float", "double") #,
+ #"size_t", "Py_ssize_t")
+
+sign_and_longness_words = ("short", "long", "signed", "unsigned")
+
+base_type_start_words = \
+ basic_c_type_names + sign_and_longness_words
+
+def p_sign_and_longness(s):
+ signed = 1
+ longness = 0
+ while s.sy == 'IDENT' and s.systring in sign_and_longness_words:
+ if s.systring == 'unsigned':
+ signed = 0
+ elif s.systring == 'signed':
+ signed = 2
+ elif s.systring == 'short':
+ longness = -1
+ elif s.systring == 'long':
+ longness += 1
+ s.next()
+ return signed, longness
+
+def p_opt_cname(s):
+ literal = p_opt_string_literal(s)
+ if literal:
+ _, cname = literal
+ else:
+ cname = None
+ return cname
+
+def p_c_declarator(s, ctx = Ctx(), empty = 0, is_type = 0, cmethod_flag = 0, nonempty = 0,
+ calling_convention_allowed = 0):
+ # If empty is true, the declarator must be empty. If nonempty is true,
+ # the declarator must be nonempty. Otherwise we don't care.
+ # If cmethod_flag is true, then if this declarator declares
+ # a function, it's a C method of an extension type.
+ pos = s.position()
+ if s.sy == '(':
+ s.next()
+ if s.sy == ')' or looking_at_type(s):
+ base = Nodes.CNameDeclaratorNode(pos, name = "", cname = None)
+ result = p_c_func_declarator(s, pos, ctx, base, cmethod_flag)
+ else:
+ result = p_c_declarator(s, ctx, empty = empty, is_type = is_type,
+ cmethod_flag = cmethod_flag, nonempty = nonempty,
+ calling_convention_allowed = 1)
+ s.expect(')')
+ else:
+ result = p_c_simple_declarator(s, ctx, empty, is_type, cmethod_flag, nonempty)
+ if not calling_convention_allowed and result.calling_convention and s.sy <> '(':
+ error(s.position(), "%s on something that is not a function"
+ % result.calling_convention)
+ while s.sy in ('[', '('):
+ pos = s.position()
+ if s.sy == '[':
+ result = p_c_array_declarator(s, result)
+ else: # sy == '('
+ s.next()
+ result = p_c_func_declarator(s, pos, ctx, result, cmethod_flag)
+ cmethod_flag = 0
+ return result
+
+def p_c_array_declarator(s, base):
+ pos = s.position()
+ s.next() # '['
+ if s.sy <> ']':
+ dim = p_expr(s)
+ else:
+ dim = None
+ s.expect(']')
+ return Nodes.CArrayDeclaratorNode(pos, base = base, dimension = dim)
+
+def p_c_func_declarator(s, pos, ctx, base, cmethod_flag):
+ # Opening paren has already been skipped
+ args = p_c_arg_list(s, ctx, cmethod_flag = cmethod_flag,
+ nonempty_declarators = 0)
+ ellipsis = p_optional_ellipsis(s)
+ s.expect(')')
+ nogil = p_nogil(s)
+ exc_val, exc_check = p_exception_value_clause(s)
+ with_gil = p_with_gil(s)
+ return Nodes.CFuncDeclaratorNode(pos,
+ base = base, args = args, has_varargs = ellipsis,
+ exception_value = exc_val, exception_check = exc_check,
+ nogil = nogil or ctx.nogil or with_gil, with_gil = with_gil)
+
+def p_c_simple_declarator(s, ctx, empty, is_type, cmethod_flag, nonempty):
+ pos = s.position()
+ calling_convention = p_calling_convention(s)
+ if s.sy == '*':
+ s.next()
+ base = p_c_declarator(s, ctx, empty = empty, is_type = is_type,
+ cmethod_flag = cmethod_flag, nonempty = nonempty)
+ result = Nodes.CPtrDeclaratorNode(pos,
+ base = base)
+ elif s.sy == '**': # scanner returns this as a single token
+ s.next()
+ base = p_c_declarator(s, ctx, empty = empty, is_type = is_type,
+ cmethod_flag = cmethod_flag, nonempty = nonempty)
+ result = Nodes.CPtrDeclaratorNode(pos,
+ base = Nodes.CPtrDeclaratorNode(pos,
+ base = base))
+ else:
+ if s.sy == 'IDENT':
+ name = s.systring
+ if is_type:
+ s.add_type_name(name)
+ if empty:
+ error(s.position(), "Declarator should be empty")
+ s.next()
+ cname = p_opt_cname(s)
+ else:
+ if nonempty:
+ error(s.position(), "Empty declarator")
+ name = ""
+ cname = None
+ result = Nodes.CNameDeclaratorNode(pos,
+ name = name, cname = cname)
+ result.calling_convention = calling_convention
+ return result
+
+def p_nogil(s):
+ if s.sy == 'IDENT' and s.systring == 'nogil':
+ s.next()
+ return 1
+ else:
+ return 0
+
+def p_with_gil(s):
+ if s.sy == 'with':
+ s.next()
+ s.expect_keyword('gil')
+ return 1
+ else:
+ return 0
+
+def p_exception_value_clause(s):
+ exc_val = None
+ exc_check = 0
+ if s.sy == 'except':
+ s.next()
+ if s.sy == '*':
+ exc_check = 1
+ s.next()
+ else:
+ if s.sy == '?':
+ exc_check = 1
+ s.next()
+ exc_val = p_simple_expr(s)
+ return exc_val, exc_check
+
+c_arg_list_terminators = ('*', '**', '.', ')')
+
+def p_c_arg_list(s, ctx = Ctx(), in_pyfunc = 0, cmethod_flag = 0, nonempty_declarators = 0,
+ kw_only = 0):
+ # Comma-separated list of C argument declarations, possibly empty.
+ # May have a trailing comma.
+ args = []
+ is_self_arg = cmethod_flag
+ while s.sy not in c_arg_list_terminators:
+ args.append(p_c_arg_decl(s, ctx, in_pyfunc, is_self_arg,
+ nonempty = nonempty_declarators, kw_only = kw_only))
+ if s.sy <> ',':
+ break
+ s.next()
+ is_self_arg = 0
+ return args
+
+def p_optional_ellipsis(s):
+ if s.sy == '.':
+ expect_ellipsis(s)
+ return 1
+ else:
+ return 0
+
+def p_c_arg_decl(s, ctx, in_pyfunc, cmethod_flag = 0, nonempty = 0, kw_only = 0):
+ pos = s.position()
+ allow_none = None
+ #not_none = 0
+ default = None
+ base_type = p_c_base_type(s, cmethod_flag)
+ declarator = p_c_declarator(s, ctx, nonempty = nonempty)
+ if s.sy in ('or', 'not'):
+ or_not = s.sy
+ s.next()
+ if s.sy == 'IDENT' and s.systring == 'None':
+ s.next()
+ else:
+ s.error("Expected 'None'")
+ if not in_pyfunc:
+ error(pos, "'%s None' only allowed in Python functions" % or_not)
+ allow_none = or_not == 'or'
+ if s.sy == '=':
+ s.next()
+ default = p_simple_expr(s)
+ return Nodes.CArgDeclNode(pos,
+ base_type = base_type,
+ declarator = declarator,
+ allow_none = allow_none,
+ default = default,
+ kw_only = kw_only)
+
+def p_api(s):
+ if s.sy == 'IDENT' and s.systring == 'api':
+ s.next()
+ return 1
+ else:
+ return 0
+
+def p_cdef_statement(s, ctx):
+ ctx = ctx(cdef_flag = 1)
+ pos = s.position()
+ ctx.visibility = p_visibility(s, ctx.visibility)
+ ctx.api = ctx.api or p_api(s)
+ if ctx.api:
+ if ctx.visibility not in ('private', 'public'):
+ error(pos, "Cannot combine 'api' with '%s'" % visibility)
+ if ctx.visibility == 'extern' and s.sy == 'from':
+ return p_cdef_extern_block(s, pos, ctx)
+ if p_nogil(s):
+ ctx.nogil = 1
+ if s.sy == ':':
+ return p_cdef_block(s, ctx)
+ elif s.sy == 'class':
+ if ctx.level not in ('module', 'module_pxd'):
+ error(pos, "Extension type definition not allowed here")
+ #if api:
+ # error(pos, "'api' not allowed with extension class")
+ return p_c_class_definition(s, pos, ctx)
+ elif s.sy == 'IDENT' and s.systring in struct_union_or_enum:
+ if ctx.level not in ('module', 'module_pxd'):
+ error(pos, "C struct/union/enum definition not allowed here")
+ #if ctx.visibility == 'public':
+ # error(pos, "Public struct/union/enum definition not implemented")
+ #if ctx.api:
+ # error(pos, "'api' not allowed with '%s'" % s.systring)
+ if s.systring == "enum":
+ return p_c_enum_definition(s, pos, ctx)
+ else:
+ return p_c_struct_or_union_definition(s, pos, ctx)
+ elif s.sy == 'pass':
+ node = p_pass_statement(s)
+ s.expect_newline('Expected a newline')
+ return node
+ else:
+ return p_c_func_or_var_declaration(s, pos, ctx)
+
+def p_cdef_block(s, ctx):
+ return p_suite(s, ctx(cdef_flag = 1))
+
+def p_cdef_extern_block(s, pos, ctx):
+ include_file = None
+ s.expect('from')
+ if s.sy == '*':
+ s.next()
+ else:
+ _, include_file = p_string_literal(s)
+ ctx = ctx(cdef_flag = 1, visibility = 'extern', extern_from = True)
+ if p_nogil(s):
+ ctx.nogil = 1
+ body = p_suite(s, ctx)
+ return Nodes.CDefExternNode(pos,
+ include_file = include_file,
+ body = body)
+
+struct_union_or_enum = (
+ "struct", "union", "enum"
+)
+
+def p_c_enum_definition(s, pos, ctx):
+ # s.sy == ident 'enum'
+ s.next()
+ if s.sy == 'IDENT':
+ name = s.systring
+ s.next()
+ s.add_type_name(name)
+ cname = p_opt_cname(s)
+ else:
+ name = None
+ cname = None
+ items = None
+ s.expect(':')
+ items = []
+ if s.sy <> 'NEWLINE':
+ p_c_enum_line(s, items)
+ else:
+ s.next() # 'NEWLINE'
+ s.expect_indent()
+ while s.sy not in ('DEDENT', 'EOF'):
+ p_c_enum_line(s, items)
+ s.expect_dedent()
+ return Nodes.CEnumDefNode(pos, name = name, cname = cname, items = items,
+ typedef_flag = ctx.typedef_flag,
+ visibility = ctx.visibility,
+ in_pxd = ctx.level == 'module_pxd')
+
+def p_c_enum_line(s, items):
+ if s.sy <> 'pass':
+ p_c_enum_item(s, items)
+ while s.sy == ',':
+ s.next()
+ if s.sy in ('NEWLINE', 'EOF'):
+ break
+ p_c_enum_item(s, items)
+ else:
+ s.next()
+ s.expect_newline("Syntax error in enum item list")
+
+def p_c_enum_item(s, items):
+ pos = s.position()
+ name = p_ident(s)
+ cname = p_opt_cname(s)
+ value = None
+ if s.sy == '=':
+ s.next()
+ value = p_simple_expr(s)
+ items.append(Nodes.CEnumDefItemNode(pos,
+ name = name, cname = cname, value = value))
+
+def p_c_struct_or_union_definition(s, pos, ctx):
+ # s.sy == ident 'struct' or 'union'
+ ctx.cplus_check(pos)
+ kind = s.systring
+ s.next()
+ module_path, name = p_qualified_name(s)
+ bases = []
+ if s.sy == '(':
+ s.next()
+ while s.sy == 'IDENT':
+ bases.append(p_qualified_name(s))
+ if s.sy <> ',':
+ break
+ s.next()
+ s.expect(')')
+ if bases and not ctx.cplus_flag:
+ error(s, "Only C++ struct may have bases")
+ cname = p_opt_cname(s)
+ s.add_type_name(name)
+ attributes = None
+ if s.sy == ':':
+ s.next()
+ s.expect('NEWLINE')
+ s.expect_indent()
+ attributes = []
+ body_ctx = Ctx()
+ while s.sy <> 'DEDENT':
+ if s.sy <> 'pass':
+ attributes.append(p_c_func_or_var_declaration(s,
+ s.position(), body_ctx))
+ else:
+ s.next()
+ s.expect_newline("Expected a newline")
+ s.expect_dedent()
+ else:
+ s.expect_newline("Syntax error in struct or union definition")
+ return Nodes.CStructOrUnionDefNode(pos,
+ name = name, cname = cname, module_path = module_path,
+ kind = kind, attributes = attributes,
+ typedef_flag = ctx.typedef_flag,
+ visibility = ctx.visibility,
+ in_pxd = ctx.level == 'module_pxd',
+ cplus_flag = ctx.cplus_flag,
+ bases = bases)
+
+def p_visibility(s, prev_visibility):
+ pos = s.position()
+ visibility = prev_visibility
+ if s.sy == 'IDENT' and s.systring in ('extern', 'public', 'readonly'):
+ visibility = s.systring
+ if prev_visibility <> 'private' and visibility <> prev_visibility:
+ s.error("Conflicting visibility options '%s' and '%s'"
+ % (prev_visibility, visibility))
+ s.next()
+ return visibility
+
+def p_c_func_or_var_declaration(s, pos, ctx):
+ cmethod_flag = ctx.level in ('c_class', 'c_class_pxd')
+ base_type = p_c_base_type(s)
+ declarator = p_c_declarator(s, ctx, cmethod_flag = cmethod_flag, nonempty = 1)
+ if s.sy == ':':
+ if ctx.level not in ('module', 'c_class'):
+ s.error("C function definition not allowed here")
+ suite = p_suite(s, Ctx(level = 'function'), with_pseudo_doc = 1)
+ result = Nodes.CFuncDefNode(pos,
+ visibility = ctx.visibility,
+ base_type = base_type,
+ declarator = declarator,
+ body = suite,
+ api = ctx.api)
+ else:
+ #if api:
+ # error(pos, "'api' not allowed with variable declaration")
+ declarators = [declarator]
+ while s.sy == ',':
+ s.next()
+ if s.sy == 'NEWLINE':
+ break
+ declarator = p_c_declarator(s, ctx, cmethod_flag = cmethod_flag, nonempty = 1)
+ declarators.append(declarator)
+ s.expect_newline("Syntax error in C variable declaration")
+ result = Nodes.CVarDefNode(pos,
+ visibility = ctx.visibility,
+ base_type = base_type,
+ declarators = declarators,
+ in_pxd = ctx.level == 'module_pxd',
+ api = ctx.api)
+ return result
+
+def p_ctypedef_statement(s, ctx):
+ # s.sy == 'ctypedef'
+ pos = s.position()
+ s.next()
+ visibility = p_visibility(s, ctx.visibility)
+ api = p_api(s)
+ ctx = ctx(typedef_flag = 1, visibility = visibility)
+ if api:
+ ctx.api = 1
+ if s.sy == 'class':
+ return p_c_class_definition(s, pos, ctx)
+ elif s.sy == 'IDENT' and s.systring in ('struct', 'union', 'enum'):
+ if s.systring == 'enum':
+ return p_c_enum_definition(s, pos, ctx)
+ else:
+ return p_c_struct_or_union_definition(s, pos, ctx)
+ else:
+ base_type = p_c_base_type(s)
+ declarator = p_c_declarator(s, ctx, is_type = 1, nonempty = 1)
+ s.expect_newline("Syntax error in ctypedef statement")
+ return Nodes.CTypeDefNode(pos,
+ base_type = base_type, declarator = declarator,
+ visibility = ctx.visibility,
+ in_pxd = ctx.level == 'module_pxd')
+
+def p_def_statement(s):
+ # s.sy == 'def'
+ pos = s.position()
+ s.next()
+ name = p_ident(s)
+ #args = []
+ s.expect('(');
+ args = p_c_arg_list(s, in_pyfunc = 1, nonempty_declarators = 1)
+ star_arg = None
+ starstar_arg = None
+ if s.sy == '*':
+ s.next()
+ if s.sy == 'IDENT':
+ star_arg = p_py_arg_decl(s)
+ if s.sy == ',':
+ s.next()
+ args.extend(p_c_arg_list(s, in_pyfunc = 1,
+ nonempty_declarators = 1, kw_only = 1))
+ elif s.sy <>')':
+ s.error("Syntax error in Python function argument list")
+ if s.sy == '**':
+ s.next()
+ starstar_arg = p_py_arg_decl(s)
+ s.expect(')')
+ if p_nogil(s):
+ error(s.pos, "Python function cannot be declared nogil")
+ doc, body = p_suite(s, Ctx(level = 'function'), with_doc = 1)
+ return Nodes.DefNode(pos, name = name, args = args,
+ star_arg = star_arg, starstar_arg = starstar_arg,
+ doc = doc, body = body)
+
+def p_py_arg_decl(s):
+ pos = s.position()
+ name = p_ident(s)
+ return Nodes.PyArgDeclNode(pos, name = name)
+
+def p_class_statement(s):
+ # s.sy == 'class'
+ pos = s.position()
+ s.next()
+ class_name = p_ident(s)
+ if s.sy == '(':
+ s.next()
+ base_list = p_simple_expr_list(s)
+ s.expect(')')
+ else:
+ base_list = []
+ doc, body = p_suite(s, Ctx(level = 'class'), with_doc = 1)
+ return Nodes.PyClassDefNode(pos,
+ name = class_name,
+ bases = ExprNodes.TupleNode(pos, args = base_list),
+ doc = doc, body = body)
+
+def p_qualified_name(s):
+ path = []
+ name = p_ident(s)
+ while s.sy == '.':
+ s.next()
+ path.append(name)
+ name = p_ident(s)
+ return path, name
+
+class CClassOptions:
+
+ objstruct_cname = None
+ typeobj_cname = None
+ no_gc = 0
+
+
+def p_c_class_definition(s, pos, ctx):
+ # s.sy == 'class'
+ s.next()
+ module_path, class_name = p_qualified_name(s)
+ if module_path and s.sy == 'IDENT' and s.systring == 'as':
+ s.next()
+ as_name = p_ident(s)
+ else:
+ as_name = class_name
+ s.add_type_name(as_name)
+ options = CClassOptions()
+ base_class_module = None
+ base_class_name = None
+ if s.sy == '(':
+ s.next()
+ base_class_path, base_class_name = p_qualified_name(s)
+ if s.sy == ',':
+ s.error("C class may only have one base class")
+ s.expect(')')
+ base_class_module = ".".join(base_class_path)
+ if s.sy == '[':
+ p_c_class_options(s, ctx, options)
+ if s.sy == ':':
+ if ctx.level == 'module_pxd':
+ body_level = 'c_class_pxd'
+ else:
+ body_level = 'c_class'
+ doc, body = p_suite(s, Ctx(level = body_level), with_doc = 1)
+ else:
+ s.expect_newline("Syntax error in C class definition")
+ doc = None
+ body = None
+ if ctx.visibility == 'extern':
+ if not module_path:
+ error(pos, "Module name required for 'extern' C class")
+ if options.typeobj_cname:
+ error(pos, "Type object name specification not allowed for 'extern' C class")
+ elif ctx.visibility == 'public':
+ if not options.objstruct_cname:
+ error(pos, "Object struct name specification required for 'public' C class")
+ if not options.typeobj_cname:
+ error(pos, "Type object name specification required for 'public' C class")
+ else:
+ if ctx.api:
+ error(pos, "Only 'public' C class can be declared 'api'")
+ return Nodes.CClassDefNode(pos,
+ visibility = ctx.visibility,
+ typedef_flag = ctx.typedef_flag,
+ api = ctx.api,
+ module_name = ".".join(module_path),
+ class_name = class_name,
+ as_name = as_name,
+ base_class_module = base_class_module,
+ base_class_name = base_class_name,
+ options = options,
+ in_pxd = ctx.level == 'module_pxd',
+ doc = doc,
+ body = body)
+
+def p_c_class_options(s, ctx, options):
+ s.expect('[')
+ while 1:
+ if s.sy <> 'IDENT':
+ break
+ if s.systring == 'object':
+ if ctx.visibility not in ('public', 'extern'):
+ error(s.position(), "Object name option only allowed for 'public' or 'extern' C class")
+ s.next()
+ options.objstruct_cname = p_ident(s)
+ elif s.systring == 'type':
+ if ctx.visibility not in ('public', 'extern'):
+ error(s.position(), "Type name option only allowed for 'public' or 'extern' C class")
+ s.next()
+ options.typeobj_cname = p_ident(s)
+ elif s.systring == 'nogc':
+ s.next()
+ options.no_gc = 1
+ else:
+ s.error("Unrecognised C class option '%s'" % s.systring)
+ if s.sy <> ',':
+ break
+ s.next()
+ s.expect(']', "Expected a C class option")
+
+def p_property_decl(s):
+ pos = s.position()
+ s.next() # 'property'
+ name = p_ident(s)
+ doc, body = p_suite(s, Ctx(level = 'property'), with_doc = 1)
+ return Nodes.PropertyNode(pos, name = name, doc = doc, body = body)
+
+def p_doc_string(s):
+ if s.sy == 'STRING' or s.sy == 'BEGIN_STRING':
+ _, result = p_cat_string_literal(s)
+ if s.sy <> 'EOF':
+ s.expect_newline("Syntax error in doc string")
+ return result
+ else:
+ return None
+
+def p_module(s, pxd):
+ s.add_type_name("object")
+ pos = s.position()
+ doc = p_doc_string(s)
+ if pxd:
+ level = 'module_pxd'
+ else:
+ level = 'module'
+ body = p_statement_list(s, Ctx(level = level))
+ if s.sy <> 'EOF':
+ s.error("Syntax error in statement [%s,%s]" % (
+ repr(s.sy), repr(s.systring)))
+ return ModuleNode(pos, doc = doc, body = body)
+
+#----------------------------------------------
+#
+# Debugging
+#
+#----------------------------------------------
+
+def print_parse_tree(f, node, level, key = None):
+ ind = " " * level
+ if node:
+ f.write(ind)
+ if key:
+ f.write("%s: " % key)
+ t = type(node)
+ if t == TupleType:
+ f.write("(%s @ %s\n" % (node[0], node[1]))
+ for i in xrange(2, len(node)):
+ print_parse_tree(f, node[i], level+1)
+ f.write("%s)\n" % ind)
+ return
+ elif isinstance(node, Node):
+ try:
+ tag = node.tag
+ except AttributeError:
+ tag = node.__class__.__name__
+ f.write("%s @ %s\n" % (tag, node.pos))
+ for name, value in node.__dict__.items():
+ if name <> 'tag' and name <> 'pos':
+ print_parse_tree(f, value, level+1, name)
+ return
+ elif t == ListType:
+ f.write("[\n")
+ for i in xrange(len(node)):
+ print_parse_tree(f, node[i], level+1)
+ f.write("%s]\n" % ind)
+ return
+ f.write("%s%s\n" % (ind, node))
+