package DX::QueryState;
+use DX::Scope;
+use DX::Hypothesis;
+use DX::SearchProcess;
+use DX::ResolvedPropositionSet;
+use DX::ActionPolicy::Allow;
+use DX::Step::MarkAsExhaustion;
+use DX::Utils qw(:builders);
use DX::Class;
-#has proposition_sequence => (
-# is => 'ro', isa => PropositionSequence, required => 1
-#);
+has predicates => (is => 'ro', isa => HashRef[Predicate], required => 1);
+
+has globals => (is => 'ro', isa => DictValue, required => 1);
+
+has proposition_sequence => (
+ is => 'ro', isa => PropositionSequence, required => 1
+);
+
+has search_process => (
+ is => 'lazy', isa => SearchProcess, builder => sub {
+ $_[0]->new_search_process_for($_[0]->proposition_sequence)
+ }, handles => { search_state => 'current_search_state' },
+);
+
+sub new_search_process_for {
+ my ($self, $prop_seq) = @_;
+ my @local_names = map { keys %{$_->introduced_names} }
+ @{$prop_seq->members};
+ my $scope = DX::Scope->new(
+ predicates => $self->predicates,
+ globals => $self->globals,
+ locals => [ dict() ],
+ lex_map => { }
+ );
+ my $hyp = DX::Hypothesis->new(
+ scope => $scope,
+ resolved_propositions => DX::ResolvedPropositionSet->new_empty,
+ actions => [],
+ action_applications => [],
+ action_policy => DX::ActionPolicy::Allow->new,
+ );
+ return DX::SearchProcess->new_for($hyp, $prop_seq);
+}
+
+sub with_additional_proposition {
+ my ($self, $new_prop) = @_;
+ my $old_prop_seq = $self->proposition_sequence;
+ my $new_prop_seq = $old_prop_seq->with_additional_proposition($new_prop);
+ my $old_ss = $self->search_process->current_search_state;
+ my %seen;
+ my $_munge; $_munge = sub {
+ my ($ss) = @_;
+ return $seen{$ss} ||= do {
+ my %but = (
+ decisions_taken => [
+ map [ $_->[0], $_->[1]->$_munge ], @{$ss->decisions_taken}
+ ]
+ );
+ if ($ss->propositions eq $old_prop_seq) {
+ $but{propositions} = $new_prop_seq;
+ }
+ foreach my $type (qw(on_solution_step on_exhaustion_step)) {
+ my $step = $ss->$type;
+ if ($step and $step->can('resume_search_state')) {
+ $but{$type}
+ = $seen{$step}
+ ||= $step->but(
+ resume_search_state => $step->resume_search_state->$_munge
+ );
+ }
+ }
+ if ((my $next_step = $ss->next_step)->can('original_search_state')) {
+ $but{next_step}
+ = $seen{$next_step}
+ ||= $next_step->but(
+ original_search_state
+ => $next_step->original_search_state->$_munge
+ );
+ }
+
+ $ss->but(%but);
+ };
+ };
+ my $munged_ss = $old_ss->$_munge;
+ undef($_munge);
+
+ my $new_ss = $munged_ss->but(
+ is_solution_state => 0,
+ next_step => DX::Step::ConsiderProposition->new(
+ proposition => $new_prop
+ ),
+ );
+
+ my $sol_sp = $self->search_process
+ ->but(current_search_state => $new_ss)
+ ->find_solution;
+ die "No solution\n" unless $sol_sp;
+ $self->but(
+ proposition_sequence => $new_prop_seq,
+ search_process => $sol_sp,
+ );
+}
+
+sub with_forced_backtrack {
+ my ($self) = @_;
+ my $next_ss = $self->search_process->find_next_solution;
+ die "No next solution\n" unless $next_ss;
+ $self->but(search_process => $next_ss);
+}
1;