disturbingly, bound values appear to actually work
[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_bindvalue {
114   my ($self, $action, $meta) = @_;
115   $self->_fmt_action_generic(BindValue => $action, $meta);
116 }
117
118 sub _fmt_action_generic {
119   my ($self, $name, $action, $meta) = @_;
120   my $path = join '.', map $self->_fmt($_, $meta), @{$action->target_path};
121   join ' ', $name, $path, $self->_fmt($action->new_value, $meta);
122 }
123
124 sub _fmt_resolvedpropositionset {
125   my ($self, $rps, $meta) = @_;
126   $self->_fmt_object([
127     map {
128       [ $_, [
129         map bless([ @$_ ], 'DX::Dependency'),
130           map @{$_}[1..$#$_],
131             @{$rps->dependencies_for($_)}
132       ] ]
133     } @{$rps->propositions},
134   ], $meta);
135 }
136
137 sub _fmt_dependency {
138   my ($self, $dep, $meta) = @_;
139   '{ '.join(' ', map $self->_fmt($_, $meta), @$dep).' }'
140 }
141
142 sub _fmt_proposition {
143   my ($self, $prop, $meta) = @_;
144   join ' ',
145     $prop->predicate,
146     map $self->_fmt($_, $meta),
147       map +((!ref($_) and $prop->introduced_names->{$_}) ? "?$_" : $_),
148         @{$prop->args};
149 }
150
151 sub _fmt_scope {
152   my ($self, $scope, $meta) = @_;
153   $self->_fmt_object([
154     [ W => $scope->globals ],
155     map [ $_ => $scope->locals->[$_] ], 0..$#{$scope->locals}
156   ], $meta);
157 }
158
159 sub _fmt_searchstate {
160   my ($self, $ss, $meta) = @_;
161   $self->_fmt_object([
162     [ alternatives => '{...}' ],
163     [ current_hypothesis => $ss->current_hypothesis ],
164   ], $meta);
165 }
166
167 1;