Skip to content

Commit ac4136c

Browse files
committed
Merge remote-tracking branch 'origin/feature/value-regex' into feature/value-regex
2 parents fcb2a89 + 5348279 commit ac4136c

File tree

6 files changed

+189
-55
lines changed

6 files changed

+189
-55
lines changed

examples/hello_world.clj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(defn hello-world[]
2+
(println "Hello, world"))
3+
4+
(hello-world)

src/main.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,26 @@ mod symbol;
2323
mod type_tag;
2424
mod util;
2525
mod value;
26+
mod user_action;
2627

2728
fn main() {
28-
//
29-
// Start repl
30-
//
29+
let cli_args: user_action::Action = user_action::parse_args( std::env::args().collect() );
30+
31+
// instantiate the core environment
3132
let repl = repl::Repl::default();
32-
repl.run();
33+
34+
match cli_args {
35+
// eval the file/script
36+
user_action::Action::RunScript(script) => {
37+
println!("{}", repl::Repl::eval_file(&repl, script.as_str()));
38+
},
39+
40+
// eval the expression
41+
user_action::Action::Evaluate(expression) => {
42+
println!("{}", repl::Repl::eval(&repl, &repl::Repl::read_string(&expression)));
43+
},
44+
45+
// Start repl
46+
user_action::Action::Nothing => { repl.run(); }
47+
}
3348
}

src/reader.rs

Lines changed: 70 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -379,68 +379,46 @@ pub fn try_read_string(input: &str) -> IResult<&str, Value> {
379379

380380
let (rest_input, _) = quotation(input)?;
381381

382+
named!(escaped_string_parser<&str, String >, escaped_transform!(take_till1!(|ch| { ch == '\\' || ch == '\"'}), '\\', alt!(
383+
tag!("t") => { |_| "\t" } |
384+
tag!("b") => { |_| "\x08" } |
385+
tag!("n") => { |_| "\n" } |
386+
tag!("r") => { |_| "\r" } |
387+
tag!("f") => { |_| "\x0C" } |
388+
tag!("'") => { |_| "'" } |
389+
tag!("\"") => { |_| "\"" } |
390+
tag!("\\") => { |_| "\\" }
391+
)));
392+
382393
named!(
383394
string_parser<&str, String>,
384395
map!(
385-
terminated!(take_until!("\""), tag("\"")),
396+
terminated!(escaped_string_parser, tag("\"")),
386397
|v| String::from(v)
387398
)
388399
);
389400

390401
to_value_parser(string_parser)(rest_input)
391402
}
392403

393-
/// Tries to parse &str into Value::Pattern
394-
/// Reader Macro for Regex
395-
/// Example Successes:
396-
/// #"this is pretty straightforward" => Value::Pattern("this is pretty straightforward")
397404
pub fn try_read_pattern(input: &str) -> IResult<&str, Value> {
398-
named!(hash_quotation<&str, &str>, preceded!(consume_clojure_whitespaces_parser, tag!("#\"")));
399-
400-
let (rest_input, _) = hash_quotation(input)?;
401-
402-
// println!("regexquoted: {:#?}", regex::Regex::quote(rest_input));
403-
let mut iterator = rest_input.escape_default();
404-
let mut prev: char = iterator.next().unwrap();
405-
let mut prev_prev_was_escape = false;
406-
let mut is_escaping = false;
407-
let mut till_quote: String = String::from(prev.to_string());
408-
println!("first char: {:#?}", till_quote);
409-
while let ch = iterator.next().unwrap() {
410-
if ch == '\\' && prev == '\\' {
411-
is_escaping = true;
412-
}
413-
println!(
414-
"LOOP: next char to handle: {:#?} prev: {:#?} is escaping {} and prev prev was escaping {}",
415-
ch, prev, is_escaping, prev_prev_was_escape
416-
);
417-
if ch == '\"' && prev == '\\' && !prev_prev_was_escape {
418-
println!(
419-
"GONNA END: next char to handle: {:#?} prev: {:#?} is escaping {}",
420-
ch, prev, is_escaping
421-
);
422-
till_quote = till_quote.trim_end_matches("\"").to_string();
423-
break;
424-
};
425-
if ch == '\"' && is_escaping {
426-
till_quote = String::from(till_quote + ch.to_string().as_str());
427-
} else if ch != '\\' {
428-
till_quote = String::from(till_quote + ch.to_string().as_str());
429-
//is_escaping = false;
430-
prev_prev_was_escape = false;
431-
}
432-
prev_prev_was_escape = is_escaping;
433-
prev = ch;
405+
named!(hash_parser<&str, &str>, preceded!(consume_clojure_whitespaces_parser, tag!("#")));
406+
407+
let (rest_input, _) = hash_parser(input)?;
408+
let (rest_input,regex_string_val) = try_read_string(rest_input)?;
409+
410+
let mut regex_string = String::from("");
411+
412+
// @TODO separate try_read_string into a parser, so we don't have to read a Value
413+
// and then unwrap it
414+
match regex_string_val {
415+
Value::String(reg_str) => { regex_string = reg_str; },
416+
_ => { panic!("try_read_string returned something that wasn't string"); }
434417
}
435-
println!("till quote: {} {:#?}", till_quote, till_quote);
436-
let to_trim = till_quote.to_owned() + "\"";
437-
println!(
438-
"rest input trimmed: {}",
439-
rest_input.trim_start_matches(&to_trim)
440-
);
441-
let regex = regex::Regex::new(till_quote.as_str()).unwrap();
418+
419+
let regex = regex::Regex::new(regex_string.as_str()).unwrap();
442420
Ok((
443-
rest_input.trim_start_matches(&to_trim),
421+
rest_input,
444422
Value::Pattern(regex),
445423
))
446424
}
@@ -921,7 +899,50 @@ mod tests {
921899
}
922900
}
923901
}
902+
mod regex_tests {
903+
use crate::reader::try_read;
904+
use crate::value::Value;
905+
906+
#[test]
907+
fn try_read_simple_regex_pattern_test() {
908+
assert_eq!(
909+
Value::Pattern(regex::Regex::new("a").unwrap()),
910+
try_read(r###"#"a" "###).ok().unwrap().1
911+
);
912+
}
913+
914+
#[test]
915+
fn try_read_regex_pattern_test() {
916+
assert_eq!(
917+
Value::Pattern(regex::Regex::new("hello").unwrap()),
918+
try_read("#\"hello\" ").ok().unwrap().1
919+
);
920+
}
921+
922+
#[test]
923+
fn try_read_regex_pattern_escaped_quote_test() {
924+
assert_eq!(
925+
Value::Pattern(regex::Regex::new("h\"e\"l\"l\"o\"").unwrap()),
926+
try_read(r#"#"h\"e\"l\"l\"o\"" something"#).ok().unwrap().1
927+
);
928+
}
929+
930+
#[test]
931+
fn try_read_regex_pattern_escaped_quote_prefixed_by_whitespace_test() {
932+
assert_eq!(
933+
Value::Pattern(regex::Regex::new("h\"e\"l\"l \"o").unwrap()),
934+
try_read(r#"#"h\"e\"l\"l \"o""#).ok().unwrap().1
935+
);
936+
}
924937

938+
#[test]
939+
fn try_read_regex_pattern_escaped_quote_suffixed_by_whitespace_test() {
940+
assert_eq!(
941+
Value::Pattern(regex::Regex::new("h\"e\"l\" l \"o").unwrap()),
942+
try_read(r#"#"h\"e\"l\" l \"o" something"#).ok().unwrap().1
943+
);
944+
}
945+
}
925946
mod consume_clojure_whitespaces_tests {
926947
use crate::reader::consume_clojure_whitespaces_parser;
927948
#[test]

src/repl.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use std::io::Write;
66

77
use crate::environment::Environment;
88
use crate::reader;
9-
use crate::value::Evaluable;
10-
use crate::value::Value;
9+
use crate::value::{Value,Evaluable,ToValue};
1110
use std::rc::Rc;
1211

1312
pub struct Repl {
@@ -84,6 +83,9 @@ impl Repl {
8483
last_val = Repl::read(&mut reader);
8584
}
8685
}
86+
pub fn eval_file(&self, filepath: &str) -> Value {
87+
self.try_eval_file(filepath).to_value()
88+
}
8789
}
8890

8991
impl Default for Repl {

src/user_action.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use std::fmt;
2+
3+
#[derive(PartialEq,Eq)]
4+
pub enum Action{
5+
RunScript(String),
6+
Evaluate(String),
7+
Nothing,
8+
}
9+
10+
impl fmt::Debug for Action {
11+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
12+
match &*self {
13+
Action::RunScript(filepath) => write!(f, "RunScript: {}", filepath),
14+
Action::Evaluate(expression) => write!(f, "Evaluate: {}", expression),
15+
Action::Nothing => write!(f, "Nothing"),
16+
}
17+
}
18+
}
19+
20+
pub fn parse_args(arguments: Vec<String>) -> Action{
21+
22+
if arguments.len() >= 2 {
23+
if arguments[1] == "-i" || arguments[1] == "--init" {
24+
return Action::RunScript(arguments[2].clone());
25+
}else if arguments[1] == "-e" || arguments[1] == "--eval" {
26+
return Action::Evaluate(arguments[2].clone());
27+
}else { Action::RunScript(arguments[1].clone()) } // for path as argument
28+
}else {
29+
return Action::Nothing;
30+
}
31+
}
32+
33+
#[cfg(test)]
34+
mod tests {
35+
mod parse_args_test {
36+
use crate::user_action;
37+
38+
#[test]
39+
fn parses_args_given_path() {
40+
let arguments = vec!["target/debug/rust_clojure".to_string(), "examples/hello_world.clj".to_string()];
41+
assert_eq!(user_action::Action::RunScript("examples/hello_world.clj".to_string()),
42+
user_action::parse_args(arguments));
43+
}
44+
45+
#[test]
46+
fn parses_args_given_i() {
47+
let arguments = vec!["target/debug/rust_clojure".to_string(), "-i".to_string(), "test.clj".to_string()];
48+
49+
assert_eq!(user_action::Action::RunScript("test.clj".to_string()),
50+
user_action::parse_args(arguments));
51+
}
52+
53+
#[test]
54+
fn parses_args_given_init() {
55+
let arguments = vec!["target/debug/rust_clojure".to_string(), "--init".to_string(), "testing.clj".to_string()];
56+
57+
assert_eq!(user_action::Action::RunScript("testing.clj".to_string()),
58+
user_action::parse_args(arguments));
59+
}
60+
61+
#[test]
62+
fn parses_args_given_e() {
63+
let arguments = vec!["target/debug/rust_clojure".to_string(), "-e".to_string(), "(+ 1 2 3)".to_string()];
64+
65+
assert_eq!(user_action::Action::Evaluate("(+ 1 2 3)".to_string()),
66+
user_action::parse_args(arguments));
67+
}
68+
69+
#[test]
70+
fn parses_args_given_eval() {
71+
let arguments = vec!["target/debug/rust_clojure".to_string(), "--eval".to_string(), "(println \"eh\")".to_string()];
72+
73+
assert_eq!(user_action::Action::Evaluate("(println \"eh\")".to_string()),
74+
user_action::parse_args(arguments));
75+
}
76+
77+
#[test]
78+
fn parses_args_given_nil() {
79+
assert_eq!(user_action::Action::Nothing, user_action::parse_args(vec!["target/debug/rust_clojure".to_string()]));
80+
}
81+
}
82+
}
83+

src/value.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use core::fmt::Display;
12
use crate::environment::Environment;
23
use crate::ifn::IFn;
34
use crate::keyword::Keyword;
@@ -623,6 +624,14 @@ impl ToValue for PersistentListMap {
623624
Value::PersistentListMap(self.clone())
624625
}
625626
}
627+
impl<T: Display> ToValue for Result<Value,T> {
628+
fn to_value(&self) -> Value {
629+
match self {
630+
Ok(val) => val.clone(),
631+
Err(err) => Value::Condition(err.to_string())
632+
}
633+
}
634+
}
626635

627636
/// Allows a type to be evaluated, abstracts evaluation
628637
///

0 commit comments

Comments
 (0)