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)
//recover, // for converting panics to python exceptions (#27719)
// -- 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

View file

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

View file

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

View file

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