Skip to content

Commit

Permalink
Change REPL implementation to linefeed crate
Browse files Browse the repository at this point in the history
- No test yet until murarth/linefeed#56 resolved
- Increase max-width rule in the formatter
- Use `map_err` for clean error propagation
  • Loading branch information
DrSensor committed Mar 17, 2019
1 parent 1c9866c commit 40d42b8
Show file tree
Hide file tree
Showing 14 changed files with 296 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
tab_spaces = 4
max_width = 100
max_width = 115
hard_tabs = true
223 changes: 223 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export MPLBACKEND="Qt5Agg"
export RUST_BACKTRACE="1"

# Start development
start: clear
Expand All @@ -10,6 +9,10 @@ check: clear
cargo check
mypy scripts

# Run with backtrace on
@trace +command:
RUST_BACKTRACE="1" just {{command}}

# Run `just +command` whenever some files is changed
@watch +command:
watchexec --restart --clear just {{command}}
Expand Down
1 change: 1 addition & 0 deletions packages/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = "2018"
[dependencies]
scdlang_xstate = { path = "../transpiler/xstate" }
atty = "*"
linefeed = "*"

[dependencies.clap]
version = "*"
Expand Down
14 changes: 4 additions & 10 deletions packages/cli/src/commands/code/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,18 @@ impl<'c> CLI<'c> for Code {
};

if args.is_present("stream") {
let file = match File::open(filepath) {
Ok(content) => content,
Err(io_error) => return Err(Error::IO(io_error)),
};
let file = File::open(filepath).map_err(Error::IO)?;
for line in BufReader::new(file).lines() {
let expression: String = line.expect(Self::NAME);
if let Err(err) = machine.insert_parse(&expression) {
println!("{}", err);
eprintln!("{}", err);
return Err(Error::Parse(expression));
}
}
} else {
let _file = match fs::read_to_string(filepath) {
Ok(content) => content,
Err(io_error) => return Err(Error::IO(io_error)),
};
let _file = fs::read_to_string(filepath).map_err(Error::IO)?;
if let Err(err) = unimplemented_ok() {
println!("{}", err);
eprintln!("{}", err);
return Err(Error::Parse(err));
}
}
Expand Down
68 changes: 44 additions & 24 deletions packages/cli/src/commands/eval/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
mod utils;

use crate::{
cli::{Result, CLI},
error::Error,
prompt,
};
use atty::Stream;
use clap::{App, ArgMatches};
use linefeed::{Interface, ReadResult};
use scdlang_xstate::*;
use std::io::{self, prelude::*};
use utils::*;

pub struct Eval;
impl<'c> CLI<'c> for Eval {
Expand All @@ -25,35 +23,57 @@ impl<'c> CLI<'c> for Eval {
}

fn invoke(args: &ArgMatches) -> Result {
let stdin = io::stdin();
let mut machine = ast::Machine::new();
let prompting = || {
if atty::is(Stream::Stdin) {
prompt(prompt::REPL).expect(Self::NAME);
}
};
let repl = Interface::new(env!("CARGO_PKG_NAME")).map_err(Error::IO)?;
let mut previously_error = false;

if atty::is(Stream::Stdin) && !args.is_present("interactive") {
println!("Press Ctrl-D to exit and print the final results");
}

// TODO: change to https://docs.rs/linefeed
prompting();
for line in stdin.lock().lines() {
let expression = line.expect(Self::NAME);
let mut parse = |expression: String| -> Result {
if !expression.is_empty() {
if let Err(err) = machine.insert_parse(expression.as_str()) {
println!("{}", err);
if args.is_present("strict") {
return Err(Error::Parse(expression));
match machine.insert_parse(expression.as_str()) {
Ok(_) => {
if args.is_present("interactive") {
println!("{}", machine);
}
if previously_error {
repl.remove_history(repl.history_len() - 1); // remove errored input
previously_error = false;
}
}
Err(err) => {
println!("{}", err);
if args.is_present("strict") {
return Err(Error::Parse(expression));
}
previously_error = true;
}
} else if args.is_present("interactive") {
println!("{}", machine);
}
repl.add_history_unique(expression);
}
Ok(())
};

// parse depend on if it's piped from another process or not
if !atty::is(Stream::Stdin) {
for line in io::stdin().lock().lines() {
parse(line.expect(Self::NAME))?;
}
} else {
println!(
"Press Ctrl-D to exit {}",
if !args.is_present("interactive") {
"and print the final results"
} else {
""
}
);

repl.set_prompt(&format!("{} ", prompt::REPL)).map_err(Error::IO)?;
while let ReadResult::Input(line) = repl.read_line().map_err(Error::IO)? {
parse(line)?;
}
prompting();
}

// print final result depend on the condition
if args.is_present("interactive") {
print!("\r")
} else if atty::isnt(Stream::Stdin) {
Expand Down
8 changes: 0 additions & 8 deletions packages/cli/src/commands/eval/utils.rs

This file was deleted.

11 changes: 8 additions & 3 deletions packages/cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@ pub fn global_reporting(err: Error) {
match err {
Error::Parse(msg) => prompting(&msg),
Error::IO(msg) => {
let no_oserr = msg.to_string().replace("os error ", "");
let arr_msg: Vec<&str> = no_oserr.split(' ').collect();
prompting(&arr_msg[..arr_msg.len() - 1].join(" "));
let sanitize_msg = remove_os_error(msg.to_string());
prompting(&sanitize_msg);
process::exit(msg.raw_os_error().unwrap())
}
}

process::exit(-1)
}

fn remove_os_error(message: String) -> String {
let no_oserr = message.replace("os error ", "");
let arr_msg: Vec<&str> = no_oserr.split(' ').collect();
arr_msg[..arr_msg.len() - 1].join(" ")
}
5 changes: 1 addition & 4 deletions packages/cli/tests/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ mod should_ok {

#[test]
fn parse_valid_file() {
let args = Some(format!(
"{file} --stream",
file = path::example("simple.scl").unwrap()
));
let args = Some(format!("{file} --stream", file = path::example("simple.scl").unwrap()));

let mut command = subcommand::code(args.as_deref()).unwrap();
command.assert().success();
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/tests/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod should_ok {
use super::*;

#[test]
#[ignore] // wait until https://github.com/murarth/linefeed/issues/56 resolved
fn non_interactive_mode() -> Result<(), Error> {
let args = None;
let mut command = subcommand::eval(args).unwrap();
Expand All @@ -27,6 +28,7 @@ mod should_ok {
}

#[test]
#[ignore] // wait until https://github.com/murarth/linefeed/issues/56 resolved
fn interactive_mode() -> Result<(), Error> {
let args = Some("--interactive");
let mut command = subcommand::eval(args).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/tests/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub mod subcommand;

#[allow(dead_code)] // false alarm on rustc 😅
pub mod regex {
pub const NOEMPTY: &str = r".*\S.*";
pub const NOEMPTY: &str = r".+\S.+";
}

#[allow(dead_code)] // false alarm on rustc 😅
Expand Down
6 changes: 1 addition & 5 deletions packages/cli/tests/utils/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ pub use predicates::path::*;
use std::{ffi::OsString, path::Path};

fn get_from(root: &str, dir: &str, filepath: &str) -> Result<String, OsString> {
Path::new(root)
.join(dir)
.join(filepath)
.into_os_string()
.into_string()
Path::new(root).join(dir).join(filepath).into_os_string().into_string()
}

#[allow(dead_code)] // false alarm on rustc 😅
Expand Down
10 changes: 2 additions & 8 deletions packages/core/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@ mod test {

#[test]
fn transition_to() -> Yes {
test::correct_expressions(&[
r#"A->B"#,
r#"Alpha-->B"#,
r#"A--->Beta"#,
r#"AlphaGo->BetaRust"#,
])
test::correct_expressions(&[r#"A->B"#, r#"Alpha-->B"#, r#"A--->Beta"#, r#"AlphaGo->BetaRust"#])
}

mod should_fail_when {
Expand All @@ -27,8 +22,7 @@ mod test {
// From https://github.com/tonsky/FiraCode 😋
test::wrong_expressions(&[
// #region transition_to
r#"A->>B"#, r#"A>->B"#, r#"A>-B"#, r#"A>>-B"#, r#"A~>B"#,
r#"A~~>B"#,
r#"A->>B"#, r#"A>->B"#, r#"A>-B"#, r#"A>>-B"#, r#"A~>B"#, r#"A~~>B"#,
// #endregion
])
}
Expand Down
12 changes: 4 additions & 8 deletions packages/transpiler/xstate/src/ast/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ pub mod pairs {
use pest::iterators::Pairs;

pub fn is_expression(pairs: &Pairs<Rule>) -> bool {
pairs
.peek()
.unwrap()
.into_inner()
.any(|pair| match pair.as_rule() {
Rule::expression => true,
_ => false,
})
pairs.peek().unwrap().into_inner().any(|pair| match pair.as_rule() {
Rule::expression => true,
_ => false,
})
}
}

Expand Down

0 comments on commit 40d42b8

Please sign in to comment.