find solution on proposition addition and output actions run
[scpubgit/DX.git] / lib / DX / ShellSession.pm
1 package DX::ShellSession;
2
3 use Tcl;
4 use Scalar::Util qw(weaken);
5 use DX::Expander;
6 use DX::Proposition;
7 use DX::Utils qw(deparse);
8 use DX::Class;
9
10 has shell_state => (is => 'rwp', required => 1, isa => ShellState);
11
12 has expander => (
13   is => 'lazy', builder => sub { DX::Expander->new(tcl => $_[0]->tcl) },
14   handles => [ qw(expand_args) ],
15 );
16
17 has tcl => (is => 'lazy', builder => sub {
18   my ($self) = @_;
19   weaken $self;
20   my $tcl = Tcl->new;
21   $tcl->CreateCommand('?' => sub {
22     $self->apply_to_state([ 'new_query_state' ], [ mode => 'query' ]);
23   });
24   $tcl->CreateCommand('?+' => sub {
25     $self->apply_to_state([ mode => 'query' ]);
26   });
27   my $qvars;
28   $tcl->CreateCommand('.' => sub {
29     $self->apply_to_state([ mode => 'shell' ]);
30     $qvars->();
31   });
32   $tcl->CreateCommand(qlist => sub {
33     push our @Result, map [ output => $_ ], @{
34       $self->shell_state->current_query_state->proposition_sequence->members
35     };
36     return;
37   });
38   $tcl->CreateCommand(qvars => $qvars = sub {
39     my $locals = $self->shell_state->current_query_state->search_state
40                       ->current_hypothesis->scope->locals->[0];
41     push our @Result, [ output => $locals ];
42     return;
43   });
44   foreach my $pred (
45     keys %{$self->shell_state->template_query_state->predicates}
46   ) {
47     $tcl->CreateCommand($pred => sub {
48       my (undef, undef, undef, @args) = @_; # ($data, $interp, $pred)
49       $self->apply_predicate($pred => @args);
50     });
51   }
52   return $tcl;
53 });
54
55 sub apply_to_state {
56   my ($self, @to_apply) = @_;
57   my $state = $self->shell_state;
58   our @Result;
59   foreach my $to_apply (@to_apply) {
60     my ($change, @args) = @$to_apply;
61     ($state, my @this_result) = $state->${\"with_${change}"}(@args);
62     push @Result, @this_result;
63   }
64   $self->_set_shell_state($state);
65   return;
66 }
67
68 sub is_complete_command_string {
69   my ($self, $string) = @_;
70   return !!$self->tcl->icall(info => complete => $string);
71 }
72
73 sub eval_command_string {
74   my ($self, $string) = @_;
75   local our @Result;
76   try {
77     $self->tcl->Eval($string);
78   } catch {
79     push @Result, [ output => $_ ];
80   };
81   return map {
82     ($_->[0] eq 'output' and ref($_->[1]))
83       ? [ output => deparse($_->[1]) ]
84       : $_
85   } @Result;
86 }
87
88 sub apply_predicate {
89   my ($self, $pred, @arg_strings) = @_;
90   die "Can't call predicate ${pred} outside a query\n"
91     unless $self->shell_state->mode eq 'query';
92   my @args = $self->expand_args(@arg_strings);
93   my ($intro, $need) = ({}, {});
94   foreach my $arg (@args) {
95     next if ref($arg);
96     # ?Foo is intro, Foo is need
97     ($arg =~ s/^\?// ? $intro : $need)->{$arg} = 1;
98   }
99   my $prop = DX::Proposition->new(
100     predicate => $pred,
101     args => \@args,
102     introduces_names => $intro,
103     requires_names => $need,
104   );
105   my $old_qstate = $self->shell_state->current_query_state;
106   my $qstate = $old_qstate->with_additional_proposition($prop);
107   my $old_action_count = @{
108     $old_qstate->search_state->current_hypothesis->actions
109   };
110   my @actions = @{$qstate->search_state->current_hypothesis->actions};
111   push our @Result,
112     map [ output => $_ ], @actions[$old_action_count..$#actions];
113   $self->_set_shell_state(
114     $self->shell_state->but(
115       current_query_state => $qstate
116     )
117   );
118   return;
119 }
120
121 1;