#
# 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))
|