should be ok
This commit is contained in:
parent
ec3d1ff791
commit
a91bcc208a
@ -1,2 +1,3 @@
|
||||
pub mod errors;
|
||||
pub mod vec;
|
||||
pub mod string;
|
||||
|
||||
2
subject_source/src/string.rs
Normal file
2
subject_source/src/string.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod utf8;
|
||||
pub mod parse;
|
||||
26
subject_source/src/string/parse.rs
Normal file
26
subject_source/src/string/parse.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use crate::vec::compute::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum ParseOperationError {
|
||||
Number(std::num::ParseFloatError),
|
||||
UnknownOperation,
|
||||
}
|
||||
|
||||
impl FromStr for Operation {
|
||||
type Err = ParseOperationError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Operation, ParseOperationError> {
|
||||
match s {
|
||||
"+" => Ok(Operation::Binary(Binary::Add)),
|
||||
"-" => Ok(Operation::Binary(Binary::Sub)),
|
||||
"*" => Ok(Operation::Binary(Binary::Mul)),
|
||||
"/" => Ok(Operation::Binary(Binary::Div)),
|
||||
s if s.starts_with(|c| "0123456789.-".contains(c)) => {
|
||||
let num = s.parse().map_err(ParseOperationError::Number)?;
|
||||
Ok(Operation::Push(num))
|
||||
}
|
||||
_ => Err(ParseOperationError::UnknownOperation),
|
||||
}
|
||||
}
|
||||
}
|
||||
4
subject_source/src/string/utf8.rs
Normal file
4
subject_source/src/string/utf8.rs
Normal file
@ -0,0 +1,4 @@
|
||||
/// Returns the char at the asked position (if not out of bound)
|
||||
pub fn char_at(s: &str, n: usize) -> Option<char> {
|
||||
s.chars().nth(n)
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Operation {
|
||||
Push(f32),
|
||||
Binary(Binary),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Binary {
|
||||
Add,
|
||||
Sub,
|
||||
|
||||
48
subject_source/tests/string_parse.rs
Normal file
48
subject_source/tests/string_parse.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use subject_source::string::parse::*;
|
||||
use subject_source::vec::compute::*;
|
||||
|
||||
#[test]
|
||||
pub fn parse_add() {
|
||||
assert_eq!("+".parse(), Ok(Operation::Binary(Binary::Add)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_sub() {
|
||||
assert_eq!("-".parse(), Ok(Operation::Binary(Binary::Sub)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_mul() {
|
||||
assert_eq!("*".parse(), Ok(Operation::Binary(Binary::Mul)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_div() {
|
||||
assert_eq!("/".parse(), Ok(Operation::Binary(Binary::Div)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_number() {
|
||||
assert_eq!("3.14".parse(), Ok(Operation::Push(3.14)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_number_negative() {
|
||||
assert_eq!("-3.14".parse(), Ok(Operation::Push(-3.14)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_bad_number() {
|
||||
match "3.14aaa".parse::<Operation>() {
|
||||
Err(ParseOperationError::Number(_)) => {}
|
||||
_ => panic!("'3.14aaa' should raise a ParseOperationError::Number error"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_bad_operation() {
|
||||
match "blabla".parse::<Operation>() {
|
||||
Err(ParseOperationError::UnknownOperation) => {}
|
||||
_ => panic!("'blabla' should raise a ParseOperationError::UnknownOperation error"),
|
||||
}
|
||||
}
|
||||
21
subject_source/tests/string_utf8.rs
Normal file
21
subject_source/tests/string_utf8.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use subject_source::string::utf8::char_at;
|
||||
|
||||
#[test]
|
||||
pub fn char_at_empty() {
|
||||
assert_eq!(char_at("", 0), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn char_at_emoji() {
|
||||
assert_eq!(char_at("🙂🙁", 1), Some('🙁'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn char_at_oob() {
|
||||
assert_eq!(char_at("abc", 3), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn char_at_ascii() {
|
||||
assert_eq!(char_at("abc", 1), Some('b'));
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
revision = "0.3.0"
|
||||
parts = ["errors", "vec"]
|
||||
parts = ["errors", "vec", "string"]
|
||||
---
|
||||
|
||||
When it comes to programming, it's all fun and games until the real world comes in and sends weird unexpected inputs to your little protege. So you better handle those cases as best as you can. There are 3 main ways of handling errors.
|
||||
|
||||
5
subject_text/string/index.md
Normal file
5
subject_text/string/index.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
name = "Strings and parsing"
|
||||
difficulty = 5
|
||||
exercises = ["utf8.md", "parse.md"]
|
||||
---
|
||||
50
subject_text/string/parse.md
Normal file
50
subject_text/string/parse.md
Normal file
@ -0,0 +1,50 @@
|
||||
---
|
||||
name = "Parse"
|
||||
file = "src/string/parse.rs"
|
||||
---
|
||||
|
||||
Parsing is the act of extracting structured information from a string of symbols. In programming we generaly parse raw bytes, or even sometimes strings of bits. In Rust, we have very rudimentary parsing utility, allowing user to parse most simple standard types from `&str`. This is encoded in the [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait.
|
||||
|
||||
Let's parse simple structured data. Remember the type we introduced in the Forth exercise ?
|
||||
|
||||
```rust
|
||||
pub enum Operation {
|
||||
Push(f32),
|
||||
Binary(Binary),
|
||||
}
|
||||
|
||||
pub enum Binary {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
}
|
||||
```
|
||||
|
||||
Let's implement `FromStr` for the `Binary` and `Operation` types. Any number should create a `Push`, and we will map `"+"` to `Add`, `"-"` to `Sub`, `"*"` to `Mul`, and `"/"` to `Div`.
|
||||
|
||||
```prototype
|
||||
use crate::vec::forth;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum ParseOperationError {
|
||||
Number(std::num::ParseFloatError),
|
||||
UnknownOperation,
|
||||
}
|
||||
|
||||
impl FromStr for Operation {
|
||||
type Err = ParseOperationError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Operation, ParseOperationError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```example
|
||||
fn main() {
|
||||
assert_eq!("123".parse(), Ok(Operation::Push(123)));
|
||||
assert_eq!("+".parse(), Ok(Operation::Binary(Binary::Add)));
|
||||
assert_eq!("foo".parse(), Err(ParseOperationError::UnknownOperation));
|
||||
}
|
||||
37
subject_text/string/utf8.md
Normal file
37
subject_text/string/utf8.md
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
name = "UTF-8"
|
||||
file = "src/string/utf8.rs"
|
||||
---
|
||||
|
||||
|
||||
We will focus on the `String` type and its borrowed variant `&str`. These are UTF-8 strings, and to enforce this, all functions that create strings can only either give valid UTF-8 strings, or fail (with the error types we encountered before).
|
||||
|
||||
> # deepening
|
||||
> A word about UTF-8:
|
||||
> It is a string format where characters (or "codepoints") are encoded using a variable number of bytes.
|
||||
> ASCII characters are a subset of UTF-8, thuss are all encoded in 1 byte, but for other characters, they
|
||||
> can take 2, 3, or 4 (maximum) bytes. Because of this, random access is difficult, because ou cannot compute
|
||||
> the position in memory of the Nth codepoint without iterating through the whole string from the start.
|
||||
>
|
||||
> This is why Rust cannot make `char` accessible with direct indexing (`[]` operator), but allow iterating over `char`s.
|
||||
|
||||
Let's implement a function accessing the Nth char of a string:
|
||||
|
||||
> # note
|
||||
> You can use [`str::chars`](https://doc.rust-lang.org/std/primitive.str.html#method.chars) to iterate over
|
||||
> a string chars. If you want some challenge, you can also read the [UTF-8 spec](https://fr.wikipedia.org/wiki/UTF-8) and iterate over single bytes of the string.
|
||||
|
||||
```prototype
|
||||
/// Returns the char at the asked position (if not out of bound)
|
||||
pub fn char_at(s: &str, n: usize) -> Option<char> {
|
||||
unimplemented!()
|
||||
}
|
||||
```
|
||||
|
||||
```example
|
||||
fn main() {
|
||||
assert_eq!(char_at("abcdef", 2), Some('c'));
|
||||
assert_eq!(char_at("", 1), None);
|
||||
assert_eq!(char_at("🧐", 0), Some('🧐'));
|
||||
}
|
||||
```
|
||||
@ -8,5 +8,8 @@
|
||||
├── vec
|
||||
│ ├── access.rs
|
||||
│ └── compute.rs
|
||||
├── string
|
||||
│ ├── utf8.rs
|
||||
│ └── parse.rs
|
||||
├── vec.rs
|
||||
└── lib.rs
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user