Skip to content

Commit 7bb9636

Browse files
author
Leo Pourcelot
committed
Bug fixed, some refactorings
A bug was introduced with last commit. User input was not wiped between two call to `read_line` in `main`. As such, everything the user entered was re-evaluated on each return keypress. This commit clears the content of user input between two `read_line` calls. `identifier_parser` was also refactored. This allows us to skip one `format!` call and many clones, making the function execution faster. Its content was also split, resulting in the creation of `first_char`, `cons_str`, `is_identifier_char` and `is_non_numeric_identifier_char`. Next commits will focus on refactoring further the content of `reader.rs`.
1 parent f04d995 commit 7bb9636

2 files changed

Lines changed: 90 additions & 17 deletions

File tree

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ use nom::Err::Incomplete;
3838
use nom::error::convert_error;
3939
use nom::Needed::Size;
4040

41-
4241
fn main()
4342
{
4443
println!("Clojure RS 0.0.1");
@@ -109,6 +108,7 @@ fn main()
109108
}
110109
}
111110
}
111+
input_buffer.clear();
112112
println!();
113113
print!("user=> ");
114114
}

src/reader.rs

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,101 @@ use std::{
3333

3434
use std::fs::File;
3535

36+
/// Returns the first character of a string slice.
37+
///
38+
/// If `input` is not empty, then its first char will be returned. Otherwise,
39+
/// `None` is returned.
40+
///
41+
/// # Panics
42+
///
43+
/// This function will panic if `input` is an empty string slice.
44+
fn first_char(input: &str) -> char {
45+
input.chars().next().unwrap()
46+
}
47+
48+
/// Same as Haskell cons operator, applied to rust strings.
49+
///
50+
/// Concatenates a `char` at the beginning of a `str`
51+
fn cons_str(head: char, tail: &str) -> String {
52+
let cap = tail.len() + head.len_utf8();
53+
let mut ret = String::with_capacity(cap);
54+
55+
ret.push(head);
56+
ret.push_str(tail);
57+
58+
ret
59+
}
60+
61+
/// Returns whether if a character can be in the tail of an identifier.
62+
///
63+
/// An identifier is composed of a head (its first char) and a tail (the other
64+
/// chars).
65+
///
66+
/// A character is an identifier char if it is alphanumeric or if it is one of:
67+
/// - `|`,
68+
/// - `?`,
69+
/// - `<`,
70+
/// - `>`,
71+
/// - `+`,
72+
/// - `-`,
73+
/// - `_`,
74+
/// - `=`,
75+
/// - `^`,
76+
/// - `%`,
77+
/// - `&`,
78+
/// - `$`,
79+
/// - `*`,
80+
/// - `!`,
81+
fn is_identifier_char(chr: char) -> bool {
82+
chr.is_alphanumeric() || "|?<>+-_=^%&$*!".contains(chr)
83+
}
84+
85+
/// Returns whether if a character can be in the head of an identifier.
86+
///
87+
/// An identifier is composed of a head (its first char) and a tail (the other
88+
/// chars).
89+
///
90+
/// A character is an identifier char if it is alphabetic or if it is one of:
91+
/// - `|`,
92+
/// - `?`,
93+
/// - `<`,
94+
/// - `>`,
95+
/// - `+`,
96+
/// - `-`,
97+
/// - `_`,
98+
/// - `=`,
99+
/// - `^`,
100+
/// - `%`,
101+
/// - `&`,
102+
/// - `$`,
103+
/// - `*`,
104+
/// - `!`,
105+
fn is_non_numeric_identifier_char(chr: char) -> bool {
106+
chr.is_alphabetic() || "|?<>+-_=^%&$*!".contains(chr)
107+
}
108+
36109
/// Parses valid Clojure identifiers
37110
/// Example Successes: ab, cat, -12+3, |blah|, <well>
38111
/// Example Failures: 'a, 12b, ,cat
39112
pub fn identifier_parser(input: &str) -> IResult<&str, String> {
40-
named!( non_numeric_identifier_char<&str, char>,
41-
alt!( one_of!("|?<>+-_=^%&$*!") |
42-
map!(take_while_m_n!(1,1,char::is_alphabetic),|ls| ls.chars().next().unwrap())));
43-
named!( identifier_char<&str, char>,
44-
alt!( one_of!("|?<>+-_=^%&$*!") |
45-
map!(take_while_m_n!(1,1,char::is_alphanumeric),|ls| ls.chars().next().unwrap())));
46-
named!( identifier_ <&str, String> ,
113+
named!(identifier_head<&str, char>,
114+
map!(
115+
take_while_m_n!(1, 1, is_non_numeric_identifier_char),
116+
first_char
117+
)
118+
);
119+
120+
named!(identifier_tail<&str, &str>, take_while!(is_identifier_char));
121+
122+
named!(identifier_ <&str, String>,
47123
do_parse!(
48-
head: non_numeric_identifier_char >>
49-
rest_input:
50-
map!(
51-
many0!(complete!(identifier_char)),
52-
String::from_iter) >>
53-
(format!("{}{}",head as char,rest_input))
54-
));
124+
head: identifier_head >>
125+
rest_input: identifier_tail >>
126+
(cons_str(head, rest_input))
127+
)
128+
);
55129

56130
identifier_(input)
57-
58131
}
59132

60133
/// Parses valid Clojure symbols, whose name is a valid identifier
@@ -227,7 +300,7 @@ pub fn try_read_list(input: &str) -> IResult<&str, Value> {
227300
}
228301

229302
pub fn try_read(input: &str) -> IResult<&str, Value> {
230-
preceded(multispace0,alt(
303+
preceded(consume_clojure_whitespaces,alt(
231304
(try_read_map,
232305
try_read_string,
233306
try_read_symbol,

0 commit comments

Comments
 (0)