Skip to content

Commit

Permalink
feat!: read commands from argument first, then read from stdin/terminal
Browse files Browse the repository at this point in the history
  • Loading branch information
dxrcy committed Dec 19, 2024
1 parent 8d30f28 commit f3c491d
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 38 deletions.
2 changes: 0 additions & 2 deletions src/debugger/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ use crate::runtime::RunState;
use crate::symbol::Span;
use crate::{dprintln, AsmParser};

// TODO(feat): Warn on `eval br* ...` and suggest `jump ...`

pub fn eval(state: &mut RunState, line: String) {
// Required to make temporarily 'static
// Automatically dropped at end of scope
Expand Down
6 changes: 3 additions & 3 deletions src/debugger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod parse;
mod source;

use self::command::{Command, Label, Location, MemoryLocation};
use self::source::{SourceMode, SourceReader};
use self::source::{Source, SourceReader};
use crate::air::AsmLine;
use crate::dprintln;
use crate::output::{Condition, Output};
Expand All @@ -19,7 +19,7 @@ pub struct DebuggerOptions {

pub struct Debugger {
status: Status,
source: SourceMode,
source: Source,

// TODO(refactor): Make private, use method to increment
pub(super) instruction_count: u32,
Expand Down Expand Up @@ -163,7 +163,7 @@ impl Debugger {
) -> Self {
Self {
status: Status::default(),
source: SourceMode::from(opts.command),
source: Source::from(opts.command),
instruction_count: 0,
was_pc_changed: true,
initial_state,
Expand Down
97 changes: 64 additions & 33 deletions src/debugger/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,19 @@ use console::Key;

use crate::{dprint, dprintln, output::Output};

// TODO(feat): If argument ends in '...' (or something) then switch to `SourceMode::Terminal` once
// arguments are exhausted

/// Read from argument first, if `Some`. Then read from stream.
#[allow(private_interfaces)] // Perhaps a bad practice
#[derive(Debug)]
pub enum SourceMode {
Argument(Argument),
Stdin(Stdin),
Terminal(Terminal),
pub struct Source {
argument: Option<Argument>,
stream: Stream,
}

/// Stdin which is not attached to a terminal, i.e. piped.
/// Stdin or interactive terminal
#[derive(Debug)]
struct Stdin {
stdin: io::Stdin,
/// Command must be stored somewhere to be referenced
buffer: String,
enum Stream {
Stdin(Stdin),
Terminal(Terminal),
}

/// Command-line argument
Expand All @@ -32,6 +28,14 @@ struct Argument {
cursor: usize,
}

/// Stdin which is not attached to a terminal, i.e. piped.
#[derive(Debug)]
struct Stdin {
stdin: io::Stdin,
/// Command must be stored somewhere to be referenced
buffer: String,
}

/// Interactive unbuffered terminal
// TODO(feat): Support CTRL+Arrow keybinds
#[derive(Debug)]
Expand All @@ -51,44 +55,71 @@ struct Terminal {
history_file: File,
}

fn echo_command_prompt(command: Option<&str>) {
// Echo prompt and command for non-terminal source
// Equivalent code found in terminal source
if !Output::is_minimal() || command.is_some() {
dprint!(Always, Normal, "\x1b[1mCommand: ");
dprintln!(
Always,
Normal,
"{}",
command.unwrap_or("\x1b[3m(end of input)").trim()
);
}
}

pub trait SourceReader {
/// `None` indicates EOF
/// Returned string slice MAY include leading or trailing whitespace
fn read(&mut self) -> Option<&str>;
}

impl SourceMode {
impl Source {
pub fn from(argument: Option<String>) -> Self {
if let Some(argument) = argument {
return SourceMode::Argument(Argument::from(argument));
Self {
argument: argument.map(|argument| Argument::from(argument)),
stream: Stream::new(),
}
}
}

impl SourceReader for Source {
fn read(&mut self) -> Option<&str> {
// Always try to read from argument first
// If argument is `None`, or if read from argument returns `None`, then read from stream
// Note that `self.argument` cannot then be set to `None`, due to lifetime of returned value
if let Some(argument) = &mut self.argument {
if let Some(command) = argument.read() {
echo_command_prompt(Some(command));
return Some(command);
}
}
self.stream.read()
}
}

impl Stream {
pub fn new() -> Self {
let stdin = io::stdin();
if stdin.is_terminal() {
return SourceMode::Terminal(Terminal::new());
return Self::Terminal(Terminal::new());
}
SourceMode::Stdin(Stdin::from(stdin))
Self::Stdin(Stdin::from(stdin))
}
}

impl SourceReader for SourceMode {
impl SourceReader for Stream {
fn read(&mut self) -> Option<&str> {
let command = match self {
Self::Argument(argument) => argument.read(),
Self::Stdin(stdin) => stdin.read(),
match self {
Self::Stdin(stdin) => {
let command = stdin.read();
echo_command_prompt(command);
command
}
// Don't echo command for terminal source, that would be redundant
Self::Terminal(terminal) => return terminal.read(),
};
// Echo prompt and command for non-terminal source
// Equivalent code found in terminal source
if !Output::is_minimal() || command.is_some() {
dprint!(Always, Normal, "\x1b[1mCommand: ");
dprintln!(
Always,
Normal,
"{}",
command.unwrap_or("\x1b[3m(end of input)").trim()
);
}
command
}
}

Expand Down

0 comments on commit f3c491d

Please sign in to comment.