--- /dev/null
+use strictures 2;
+use Module::Runtime qw(use_module);
+BEGIN { *u = \&use_module }
+
+use_module('DX::ShellFrontend')->new(
+ session_mode => 'shell',
+ session => use_module('DX::ShellSession')->new(
+ shell_state => use_module('DX::ShellState')->new(
+ mode => 'shell',
+ template_query_state => use_module('DX::QueryState')->new
+ )
+ ),
+)->repl
--- /dev/null
+package DX::PropositionSequence;
+
+use Types::Standard qw(ArrayRef);
+use DX::Class;
+
+has members => (is => 'ro', isa => ArrayRef[Proposition], required => 1);
+
+sub new_empty {
+ shift->new(members => []);
+}
+
+sub but_append_proposition {
+ my ($self, $prop) = @_;
+ $self->but(members => [ @{$self->members}, $prop ];
+}
+
+1;
--- /dev/null
+package DX::QueryState;
+
+use DX::Class;
+
+#has proposition_sequence => (
+# is => 'ro', isa => PropositionSequence, required => 1
+#);
+
+1;
--- /dev/null
+package DX::ShellFrontend;
+
+use IO::Handle;
+use Caroline;
+use DX::Class;
+
+has session => (
+ is => 'ro', required => 1,
+ handles => [ qw(is_complete_command_string eval_command_string) ]
+);
+
+has session_mode => (is => 'rwp', required => 1);
+
+has readline => (is => 'lazy', builder => sub { Caroline->new });
+
+sub BUILD { STDOUT->autoflush(1) }
+
+sub repl {
+ my ($self) = @_;
+ while (1) {
+ last unless $self->rep
+ }
+}
+
+sub rep {
+ my ($self) = @_;
+ return unless defined(my $command = $self->read_command);
+ my @result = $self->eval_command_string($command);
+ $self->process_result(@result);
+ return 1;
+}
+
+sub read_command {
+ my ($self) = @_;
+ my $base_prompt = $self->session_mode eq 'shell' ? '$ ' : '? ';
+ my $rl = $self->readline;
+ return unless defined(my $command = $rl->readline($base_prompt));
+ while (not $self->is_complete_command_string($command)) {
+ $command .= $rl->readline('> ');
+ }
+ $rl->history_add($command);
+ return $command;
+}
+
+sub process_result {
+ my ($self, @result) = @_;
+ foreach my $res (@result) {
+ my ($type, $payload) = @$res;
+ $self->${\($self->can("process_${type}_result")||die)}($payload);
+ }
+}
+
+sub process_mode_result {
+ my ($self, $mode) = @_;
+ $self->_set_session_mode($mode);
+}
+
+sub process_output_result {
+ my ($self, $output) = @_;
+ print $output;
+}
+
+1;
--- /dev/null
+package DX::ShellSession;
+
+use Tcl;
+use Scalar::Util qw(weaken);
+use DX::Class;
+
+has shell_state => (is => 'rwp', required => 1, isa => ShellState);
+
+has tcl => (is => 'lazy', builder => sub {
+ my ($self) = @_;
+ weaken $self;
+ my $tcl = Tcl->new;
+ $tcl->CreateCommand('?' => sub {
+ $self->apply_to_state([ 'new_query_state' ], [ mode => 'query' ])
+ });
+ $tcl->CreateCommand('?+' => sub {
+ $self->apply_to_state([ mode => 'query' ])
+ });
+ $tcl->CreateCommand('.' => sub {
+ $self->apply_to_state([ mode => 'shell' ])
+ });
+ return $tcl;
+});
+
+sub apply_to_state {
+ my ($self, @to_apply) = @_;
+ my $state = $self->shell_state;
+ our @Result;
+ foreach my $to_apply (@to_apply) {
+ my ($change, @args) = @$to_apply;
+ ($state, my @this_result) = $state->${\"with_${change}"}(@args);
+ push @Result, @this_result;
+ }
+ $self->_set_shell_state($state);
+ return;
+}
+
+sub is_complete_command_string {
+ my ($self, $string) = @_;
+ return !!$self->tcl->icall(info => complete => $string);
+}
+
+sub eval_command_string {
+ my ($self, $string) = @_;
+ local our @Result;
+ $self->tcl->Eval($string);
+ return @Result;
+}
+
+1;
--- /dev/null
+package DX::ShellState;
+
+use DX::Class;
+
+has template_query_state => (
+ is => 'ro', required => 1, isa => QueryState
+);
+
+has current_query_state => (
+ is => 'ro', predicate => 1, isa => QueryState
+);
+
+has mode => (is => 'ro', required => 1);
+
+sub new_query_state { $_[0]->template_query_state }
+
+sub with_new_query_state {
+ my ($self) = @_;
+ $self->but(
+ current_query_state => $self->new_query_state
+ );
+}
+
+sub with_mode {
+ my ($self, $new_mode) = @_;
+ return (
+ $self->but(mode => $new_mode),
+ [ mode => $new_mode ],
+ );
+}
+
+1;
-declare => (
(our @CLASSES = qw(
Hypothesis Scope ResolvedPropositionSet Proposition DependencyMap
+ PropositionSequence QueryState ShellState ShellSession
)),
(our @ROLES = qw(
Step Action ActionPolicy Predicate Value