c3-mro.pl 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #!/usr/bin/env perl
  2. # Abstract: Check if c3 mro is well understood.
  3. # License: AGL, see LICENSE file for copyright and license details.
  4. #
  5. # General structure of a small DSL for defining classes with their
  6. # methods and inheritance relationships. This program parses the
  7. # DSL following __DATA__ and perform series of tests of c3 method
  8. # resolution with validation done with the help of Perl's c3 mro.
  9. #
  10. # CLASS_NAME: METHOD_NAME
  11. # SUBCLASS_NAME -> ( SUPERCLASS_NAME ... ) : METHOD_NAME ...
  12. #
  13. # Example:
  14. # O: meth1 meth2 meth3
  15. # A -> (O) : meth2
  16. # B -> (O) : meth1
  17. # C -> (A B) : meth2
  18. #
  19. # The above example defines a section, sections are separated by
  20. # the token '###'. Sections are tested individually.
  21. use strict;
  22. use warnings;
  23. use feature qw(say);
  24. use Data::Dumper;
  25. sub parse_class_defs_section {
  26. my $def_str = shift;
  27. my $defs;
  28. while ($def_str =~ /\G ([A-Z]) (?: \h+ -> \h+ \( (?<s>[A-Z](?:\h++[A-Z])*) \) )? (?: \h+ : \h+ (?<m>\w+(?:\h++\w++)*) )? \s* /gx) {
  29. $defs->{$1} = undef;
  30. if (defined $+{s}) {
  31. my @supers = split " ", $+{s};
  32. my @undefs = grep { !exists $defs->{$_} } @supers;
  33. die "undefined class(es): [@undefs]\n" if @undefs;
  34. $defs->{$1}{s} = [@supers];
  35. }
  36. $defs->{$1}{m} = [ split " ", $+{m} ] if defined($+{m});
  37. }
  38. return $defs;
  39. }
  40. sub parse_class_defs_sections {
  41. my $data = shift;
  42. my $sections;
  43. push @$sections, parse_class_defs_section($1) while $data =~ /\G\s*(.+?)(?:###|\z)/gs;
  44. return $sections;
  45. }
  46. sub gen_classdefs_perl_code {
  47. my $sections = shift;
  48. my ($section_codes, $perl_code);
  49. foreach my $section (@$sections) {
  50. $perl_code .= "\n\nuse feature qw(say);\n\n";
  51. foreach my $class (sort keys %$section) {
  52. my $class_info = $section->{$class};
  53. $perl_code .= "package $class {\n\n";
  54. $perl_code .= "\tuse parent -norequire qw(@{$class_info->{s}});\n\n" if exists $class_info->{s};
  55. if (exists $class_info->{m}) {
  56. foreach my $method (@{$class_info->{m}}) {
  57. my $name = $method =~ s/@$//r;
  58. $perl_code .= "\tsub $name { say 'A::$name'; " . ( $method =~ /@$/ ? "'$class'->next::method(); }" : "; }" );
  59. $perl_code .= "\n\n";
  60. $perl_code .= "}\n\n";
  61. }
  62. }
  63. }
  64. push @$section_codes, [ $section, $perl_code ];
  65. }
  66. return $section_codes;
  67. }
  68. sub c3_linearize {
  69. my $section = shift;
  70. my %linearized;
  71. foreach my $class (sort keys %$section) {
  72. }
  73. }
  74. {
  75. local $/;
  76. my $sections = parse_class_defs_sections(<DATA>);
  77. say Dumper gen_perl_code($sections);
  78. }
  79. __DATA__
  80. O : meth1 meth2 meth3
  81. A -> (O) : meth2
  82. B -> (O) : meth1
  83. C -> (A B) : meth2
  84. ###
  85. O