You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2143 lines
64 KiB
2143 lines
64 KiB
#
|
|
# 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))
|
|
|