ff590f928725f794aff5d911551ac629f773db21
[scpubgit/DX.git] / lib / DX / Deparse.pm
1 package DX::Deparse;
2
3 use DX::Class;
4
5 sub indent_by { '    ' }
6
7 sub max_width { 78 }
8
9 sub _inner_meta {
10   my ($self, $ometa) = @_;
11   +{ %$ometa, width_left => $ometa->{width_left} - length $self->indent_by };
12 }
13
14 sub _indent_one {
15   my ($self, $to_indent) = @_;
16   $to_indent =~ s/^/${\$self->indent_by}/mg;
17   $to_indent;
18 }
19
20 sub fmt {
21   my ($self, $thing) = @_;
22   $self->_fmt($thing, { width_left => $self->max_width })."\n";
23 }
24
25 sub _fmt {
26   my ($self, $thing, $meta) = @_;
27   return '{}' unless defined($thing);
28   return $thing unless ref($thing);
29   my $type = join'_', split '::', lc +(ref($thing) =~ /^(?:DX::)?(.*)/)[0];
30   $self->${\"_fmt_${type}"}($thing, $meta);
31 }
32
33 sub _fmt_value_dict {
34   my ($self, $dict, $meta) = @_;
35   my $chunks = $self->_fmt_pairs([
36     map [ $_, $dict->get_member_at($_) ], $dict->index_list
37   ], $meta);
38   return '{{ }}' unless $chunks =~ /\S/;
39   s/^\s+//, s/\n\s+/ /g for (my $maybe = $chunks);
40   return "{{ ${maybe} }}" if length($maybe) < $meta->{width_left};
41   "{{\n${chunks}\n}}";
42 }
43
44 sub _fmt_pairs {
45   my ($self, $pairs, $ometa) = @_;
46   my $imeta = $self->_inner_meta($ometa);
47   $self->_indent_one(join "\n", map {
48     join ' ', map $self->_fmt($_, $imeta), @$_
49   } @$pairs);
50 }
51
52 sub _fmt_value_string {
53   my ($self, $string) = @_;
54   # TODO: multiline handling
55   my $val = $string->string_value;
56   if ($val =~ /^\w+$/) {
57     qq{'${val}'}
58   } else {
59     qq{{'${val}'}}
60   }
61 }
62
63 sub _fmt_value_number { $_[1]->number_value }
64
65 sub _fmt_value_true { 'true' }
66
67 sub _fmt_value_false { 'false' }
68
69 sub _fmt_value_unset { 'unset' }
70
71 sub _fmt_object {
72   my ($self, $pairs, $meta) = @_;
73   my $chunks = $self->_fmt_pairs($pairs, $meta);
74   return '{ }' unless $chunks =~ /\S/;
75   s/^\s+//, s/\n\s+/ /g for (my $maybe = $chunks);
76   return "{ ${maybe} }" if length($maybe) < $meta->{width_left};
77   "{\n${chunks}\n}"
78 }
79
80 sub _fmt_hypothesis {
81   my ($self, $hyp, $meta) = @_;
82   $self->_fmt_object([
83     map [ $_ => $hyp->$_ ],
84       qw(actions outstanding_propositions resolved_propositions scope)
85   ], $meta);
86 }
87
88 sub _fmt_array {
89   my ($self, $ary, $ometa) = @_;
90   my $imeta = $self->_inner_meta($ometa);
91   my $chunks = $self->_indent_one(
92     join "\n", map $self->_fmt($_, $imeta), @$ary
93   );
94   return '{ }' unless $chunks =~ /\S/;
95   "{\n${chunks}\n}";
96 }
97
98 sub _fmt_glob {
99   my ($self, $glob, $meta) = @_;
100   return ((''.*$glob) =~ /::([A-Z_]+)$/)[0];
101 }
102
103 sub _fmt_action_setvalue {
104   my ($self, $action, $meta) = @_;
105   $self->_fmt_action_generic(SetValue => $action, $meta);
106 }
107
108 sub _fmt_action_addvalue {
109   my ($self, $action, $meta) = @_;
110   $self->_fmt_action_generic(AddValue => $action, $meta);
111 }
112
113 sub _fmt_action_generic {
114   my ($self, $name, $action, $meta) = @_;
115   my $path = join '.', map $self->_fmt($_, $meta), @{$action->target_path};
116   join ' ', $name, $path, $self->_fmt($action->new_value, $meta);
117 }
118
119 sub _fmt_resolvedpropositionset {
120   my ($self, $rps, $meta) = @_;
121   $self->_fmt_object([
122     map {
123       [ $_, [
124         map bless([ @$_ ], 'DX::Dependency'),
125           map @{$_}[1..$#$_],
126             @{$rps->dependencies_for($_)}
127       ] ]
128     } @{$rps->propositions},
129   ], $meta);
130 }
131
132 sub _fmt_dependency {
133   my ($self, $dep, $meta) = @_;
134   '{ '.join(' ', map $self->_fmt($_, $meta), @$dep).' }'
135 }
136
137 sub _fmt_proposition {
138   my ($self, $prop, $meta) = @_;
139   join ' ',
140     $prop->predicate,
141     map $self->_fmt($_, $meta),
142       map +((!ref($_) and $prop->introduced_names->{$_}) ? "?$_" : $_),
143         @{$prop->args};
144 }
145
146 sub _fmt_scope {
147   my ($self, $scope, $meta) = @_;
148   $self->_fmt_object([
149     [ W => $scope->globals ],
150     map [ $_ => $scope->locals->[$_] ], 0..$#{$scope->locals}
151   ], $meta);
152 }
153
154 sub _fmt_searchstate {
155   my ($self, $ss, $meta) = @_;
156   $self->_fmt_object([
157     [ alternatives => '{...}' ],
158     [ current_hypothesis => $ss->current_hypothesis ],
159   ], $meta);
160 }
161
162 1;