Share macro code between tp_new and other class methods.

This commit is contained in:
Daniel Grunwald 2016-04-17 18:04:58 +02:00
parent 79707c03d2
commit a14e3d2b77
4 changed files with 172 additions and 125 deletions

View file

@ -26,6 +26,8 @@
shared, // for std::ptr::Shared (#27730) shared, // for std::ptr::Shared (#27730)
//recover, // for converting panics to python exceptions (#27719) //recover, // for converting panics to python exceptions (#27719)
// -- TODO wait for stable release and promote recover code from cfg(nightly) // -- TODO wait for stable release and promote recover code from cfg(nightly)
// -- TODO remove <DUMMY> hack when it's no longer necessary on stable
))] ))]
#![allow(unused_imports)] // because some imports are only necessary with python 2.x or 3.x #![allow(unused_imports)] // because some imports are only necessary with python 2.x or 3.x

View file

@ -4,6 +4,9 @@
This python script generates the py_class_impl! macro. This python script generates the py_class_impl! macro.
""" """
from collections import namedtuple
import sys
header = ''' header = '''
// Copyright (c) 2016 Daniel Grunwald // Copyright (c) 2016 Daniel Grunwald
// //
@ -230,47 +233,142 @@ def data_decl():
}}; }};
''') ''')
def tp_new(with_params): indentation = [' ']
if with_params: last_char = '\n'
param_capture = ', $($p:tt)+'
tp_new = '''py_argparse_parse_plist_impl!{ def write(text):
py_class_wrap_newfunc {$class::__new__} global last_char
[] ($($p)+,) for line in text.splitlines(True):
}''' line = line.lstrip(' ')
impl = '''py_argparse_parse_plist_impl!{ if len(line.strip()) == 0 and last_char == '\n':
py_class_impl_item { $class, $py, __new__($cls: &$crate::PyType,) $res_type; { $($body)* } } continue
[] ($($p)+,) if last_char == '\n':
}''' initial_closing = 0
print(' // def __new__(cls, params)') for c in line:
if c in ']}':
initial_closing += 1
else:
break
if initial_closing:
sys.stdout.write(''.join(indentation[:-initial_closing]))
else:
sys.stdout.write(''.join(indentation))
elif last_char not in ' \n' and len(line) > 0 and line[0] not in ' \n;':
sys.stdout.write(' ')
sys.stdout.write(line)
min_indent_level = len(indentation)
for c in line:
if c in '[{':
if len(indentation) > min_indent_level:
indentation.append('')
else:
indentation.append(' ')
elif c in ']}':
indentation.pop()
if len(indentation) < min_indent_level:
min_indent_level = len(indentation)
last_char = line[-1]
Slot = namedtuple('Slot', ['slot_type', 'slot_name'])
def generate_case(pattern, new_impl=None, new_slots=None, new_members=None):
write('{ $class:ident $py:ident')
write('$info:tt')
if new_slots:
write('\n/* slots: */ {\n')
if any(s.slot_type == 'type_slots' for s, v in new_slots):
write('\n/* type_slots */ [ $( $type_slot_name:ident : $type_slot_value:expr, )* ]\n')
else:
write('$type_slots:tt')
write('$as_number:tt')
write('$as_sequence:tt')
write('\n}\n')
else: else:
param_capture = '' write('$slots:tt')
tp_new = 'py_class_wrap_newfunc!($class::__new__ [])' if new_impl is not None:
impl = 'py_class_impl_item! { $class, $py, __new__($cls: &$crate::PyType,) $res_type; { $($body)* } [] }' write('\n{ $( $imp:item )* }\n')
print(' // def __new__(cls)') else:
print(''' { $class:ident $py:ident $info:tt write('$impls:tt')
/* slots: */ { if new_members:
/* type_slots */ [ $( $slot_name:ident : $slot_value:expr, )* ] write('\n{ $( $member_name:ident = $member_expr:expr; )* }')
$as_number:tt $as_sequence:tt else:
} write('$members:tt')
{ $( $imp:item )* } $members:tt; write(';\n')
def __new__ ($cls:ident%s) write(pattern)
-> $res_type:ty { $( $body:tt )* } $($tail:tt)* write('$($tail:tt)*\n')
} => { py_class_impl! { write('} => { py_class_impl! {\n')
$class $py $info write('$class $py')
/* slots: */ { write('$info')
/* type_slots */ [ if new_slots:
$( $slot_name : $slot_value, )* write('\n/* slots: */ {\n')
tp_new: Some(%s), if any(s.slot_type == 'type_slots' for s, v in new_slots):
] write('\n/* type_slots */ [\n')
$as_number $as_sequence write('$( $type_slot_name : $type_slot_value, )*\n')
} for s, v in new_slots:
/* impl: */ { if s.slot_type == 'type_slots':
$($imp)* write('%s: %s,\n' % (s.slot_name, v))
%s write(']\n')
} else:
$members; write('$type_slots')
$($tail)* write('$as_number')
}};''' % (param_capture, tp_new, impl)) write('$as_sequence')
write('\n}\n')
else:
write('$slots')
if new_impl is not None:
write('\n/* impl: */ {\n')
write('$($imp)*\n')
write(new_impl)
write('\n}\n')
else:
write('$impls')
if new_members:
write('\n/* members: */ {\n')
write('$( $member_name = $member_expr; )*\n')
for name, val in new_members:
write('%s = %s;\n' % (name, val))
write('}')
else:
write('$members')
write('; $($tail)*\n')
write('}};\n')
def class_method(decoration='', special_name=None,
slot=None, add_member=False, value_macro=None, value_args=None):
assert(slot is None or isinstance(slot, Slot))
name_pattern = special_name or '$name:ident'
name_use = special_name or '$name'
def impl(with_params):
if with_params:
param_pattern = ', $($p:tt)+'
impl = '''py_argparse_parse_plist_impl!{
py_class_impl_item { $class, $py, %s($cls: &$crate::PyType,) $res_type; { $($body)* } }
[] ($($p)+,)
}''' % name_use
value = 'py_argparse_parse_plist_impl!{%s {%s} [] ($($p)+,)}' \
% (value_macro, value_args)
else:
param_pattern = ''
impl = 'py_class_impl_item! { $class, $py,%s($cls: &$crate::PyType,) $res_type; { $($body)* } [] }' \
% name_use
value = '%s!{%s []}' % (value_macro, value_args)
pattern = '%s def %s ($cls:ident%s) -> $res_type:ty { $( $body:tt )* }' \
% (decoration, name_pattern, param_pattern)
slots = []
if slot is not None:
slots.append((slot, value))
members = []
if add_member:
members.append((name_use, value))
generate_case(pattern, new_impl=impl, new_slots=slots, new_members=members)
impl(False) # without parameters
impl(True) # with parameters
def tp_new():
class_method(special_name='__new__',
slot=Slot('type_slots', 'tp_new'),
value_macro='py_class_wrap_newfunc',
value_args='$class::__new__')
def traverse_and_clear(): def traverse_and_clear():
print(''' print('''
@ -342,7 +440,7 @@ def traverse_and_clear():
$($tail)* $($tail)*
}};''') }};''')
general_cases = ''' instance_method = '''
// def instance_method(&self) // def instance_method(&self)
{ $class:ident $py:ident $info:tt $slots:tt { $class:ident $py:ident $info:tt $slots:tt
{ $( $imp:item )* } { $( $imp:item )* }
@ -385,50 +483,9 @@ general_cases = '''
}; };
$($tail)* $($tail)*
}}; }};
'''
// @classmethod def class_method(cls) static_method = '''
{ $class:ident $py:ident $info:tt $slots:tt
{ $( $imp:item )* }
{ $( $member_name:ident = $member_expr:expr; )* };
@classmethod def $name:ident ($cls:ident)
-> $res_type:ty { $( $body:tt )* } $($tail:tt)*
} => { py_class_impl! {
$class $py $info $slots
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, $name($cls: &$crate::PyType,) $res_type; { $($body)* } [] }
}
/* members: */ {
$( $member_name = $member_expr; )*
$name = py_class_class_method!{$py, $class::$name []};
};
$($tail)*
}};
// @classmethod def class_method(cls, params)
{ $class:ident $py:ident $info:tt $slots:tt
{ $( $imp:item )* }
{ $( $member_name:ident = $member_expr:expr; )* };
@classmethod def $name:ident ($cls:ident, $($p:tt)+)
-> $res_type:ty { $( $body:tt )* } $($tail:tt)*
} => { py_class_impl! {
$class $py $info $slots
/* impl: */ {
$($imp)*
py_argparse_parse_plist_impl!{
py_class_impl_item { $class, $py, $name($cls: &$crate::PyType,) $res_type; { $($body)* } }
[] ($($p)+,)
}
}
/* members: */ {
$( $member_name = $member_expr; )*
$name = py_argparse_parse_plist_impl!{
py_class_class_method {$py, $class::$name}
[] ($($p)+,)
};
};
$($tail)*
}};
// @staticmethod def static_method(params) // @staticmethod def static_method(params)
{ $class:ident $py:ident $info:tt $slots:tt { $class:ident $py:ident $info:tt $slots:tt
{ $( $imp:item )* } { $( $imp:item )* }
@ -627,12 +684,16 @@ def main():
print(macro_start) print(macro_start)
print(base_case) print(base_case)
data_decl() data_decl()
tp_new(False) tp_new()
tp_new(True)
traverse_and_clear() traverse_and_clear()
for name, f in sorted(special_names.items()): for name, f in sorted(special_names.items()):
f(name) f(name)
print(general_cases) print(instance_method)
class_method(decoration='@classmethod',
add_member=True,
value_macro='py_class_class_method',
value_args='$py, $class::$name')
print(static_method)
print(macro_end) print(macro_end)
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -224,49 +224,43 @@ macro_rules! py_class_impl {
$($tail)* $($tail)*
}}; }};
// def __new__(cls)
{ $class:ident $py:ident $info:tt { $class:ident $py:ident $info:tt
/* slots: */ { /* slots: */ {
/* type_slots */ [ $( $slot_name:ident : $slot_value:expr, )* ] /* type_slots */ [ $( $type_slot_name:ident : $type_slot_value:expr, )* ]
$as_number:tt $as_sequence:tt $as_number:tt $as_sequence:tt
} }
{ $( $imp:item )* } $members:tt; { $( $imp:item )* }
def __new__ ($cls:ident) $members:tt;
-> $res_type:ty { $( $body:tt )* } $($tail:tt)* def __new__ ($cls:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
} => { py_class_impl! { } => { py_class_impl! {
$class $py $info $class $py $info
/* slots: */ { /* slots: */ {
/* type_slots */ [ /* type_slots */ [
$( $slot_name : $slot_value, )* $( $type_slot_name : $type_slot_value, )*
tp_new: Some(py_class_wrap_newfunc!($class::__new__ [])), tp_new: py_class_wrap_newfunc!{$class::__new__ []},
] ]
$as_number $as_sequence $as_number $as_sequence
} }
/* impl: */ { /* impl: */ {
$($imp)* $($imp)*
py_class_impl_item! { $class, $py, __new__($cls: &$crate::PyType,) $res_type; { $($body)* } [] } py_class_impl_item! { $class, $py,__new__($cls: &$crate::PyType,) $res_type; { $($body)* } [] }
} }
$members; $members; $($tail)*
$($tail)*
}}; }};
// def __new__(cls, params)
{ $class:ident $py:ident $info:tt { $class:ident $py:ident $info:tt
/* slots: */ { /* slots: */ {
/* type_slots */ [ $( $slot_name:ident : $slot_value:expr, )* ] /* type_slots */ [ $( $type_slot_name:ident : $type_slot_value:expr, )* ]
$as_number:tt $as_sequence:tt $as_number:tt $as_sequence:tt
} }
{ $( $imp:item )* } $members:tt; { $( $imp:item )* }
def __new__ ($cls:ident, $($p:tt)+) $members:tt;
-> $res_type:ty { $( $body:tt )* } $($tail:tt)* def __new__ ($cls:ident, $($p:tt)+) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
} => { py_class_impl! { } => { py_class_impl! {
$class $py $info $class $py $info
/* slots: */ { /* slots: */ {
/* type_slots */ [ /* type_slots */ [
$( $slot_name : $slot_value, )* $( $type_slot_name : $type_slot_value, )*
tp_new: Some(py_argparse_parse_plist_impl!{ tp_new: py_argparse_parse_plist_impl!{py_class_wrap_newfunc {$class::__new__} [] ($($p)+,)},
py_class_wrap_newfunc {$class::__new__}
[] ($($p)+,)
}),
] ]
$as_number $as_sequence $as_number $as_sequence
} }
@ -277,8 +271,7 @@ macro_rules! py_class_impl {
[] ($($p)+,) [] ($($p)+,)
} }
} }
$members; $members; $($tail)*
$($tail)*
}}; }};
// def __traverse__(self, visit) // def __traverse__(self, visit)
@ -992,30 +985,25 @@ macro_rules! py_class_impl {
$($tail)* $($tail)*
}}; }};
// @classmethod def class_method(cls)
{ $class:ident $py:ident $info:tt $slots:tt { $class:ident $py:ident $info:tt $slots:tt
{ $( $imp:item )* } { $( $imp:item )* }
{ $( $member_name:ident = $member_expr:expr; )* }; { $( $member_name:ident = $member_expr:expr; )* };
@classmethod def $name:ident ($cls:ident) @classmethod def $name:ident ($cls:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
-> $res_type:ty { $( $body:tt )* } $($tail:tt)*
} => { py_class_impl! { } => { py_class_impl! {
$class $py $info $slots $class $py $info $slots
/* impl: */ { /* impl: */ {
$($imp)* $($imp)*
py_class_impl_item! { $class, $py, $name($cls: &$crate::PyType,) $res_type; { $($body)* } [] } py_class_impl_item! { $class, $py,$name($cls: &$crate::PyType,) $res_type; { $($body)* } [] }
} }
/* members: */ { /* members: */ {
$( $member_name = $member_expr; )* $( $member_name = $member_expr; )*
$name = py_class_class_method!{$py, $class::$name []}; $name = py_class_class_method!{$py, $class::$name []};
}; }; $($tail)*
$($tail)*
}}; }};
// @classmethod def class_method(cls, params)
{ $class:ident $py:ident $info:tt $slots:tt { $class:ident $py:ident $info:tt $slots:tt
{ $( $imp:item )* } { $( $imp:item )* }
{ $( $member_name:ident = $member_expr:expr; )* }; { $( $member_name:ident = $member_expr:expr; )* };
@classmethod def $name:ident ($cls:ident, $($p:tt)+) @classmethod def $name:ident ($cls:ident, $($p:tt)+) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
-> $res_type:ty { $( $body:tt )* } $($tail:tt)*
} => { py_class_impl! { } => { py_class_impl! {
$class $py $info $slots $class $py $info $slots
/* impl: */ { /* impl: */ {
@ -1027,12 +1015,8 @@ macro_rules! py_class_impl {
} }
/* members: */ { /* members: */ {
$( $member_name = $member_expr; )* $( $member_name = $member_expr; )*
$name = py_argparse_parse_plist_impl!{ $name = py_argparse_parse_plist_impl!{py_class_class_method {$py, $class::$name} [] ($($p)+,)};
py_class_class_method {$py, $class::$name} }; $($tail)*
[] ($($p)+,)
};
};
$($tail)*
}}; }};
// @staticmethod def static_method(params) // @staticmethod def static_method(params)

View file

@ -106,6 +106,6 @@ macro_rules! py_class_wrap_newfunc {
}) })
}) })
} }
wrap_newfunc::<()> Some(wrap_newfunc::<()>)
}} }}
} }