Match alphabetic characters directly, make stuff private

This commit is contained in:
Kasper 2021-07-06 20:25:29 +02:00
parent c3f7166d28
commit c4d1cb1371
3 changed files with 50 additions and 33 deletions

View File

@ -32,7 +32,7 @@ Add `cpc` as a dependency in `Cargo.toml`.
## API Usage ## API Usage
```rust ```rust
use cpc::{eval}; use cpc::eval;
use cpc::units::Unit; use cpc::units::Unit;
match eval("3m + 1cm", true, Unit::Celsius, false) { match eval("3m + 1cm", true, Unit::Celsius, false) {

View File

@ -13,17 +13,21 @@ use crate::units::Unit;
use crate::units::Unit::*; use crate::units::Unit::*;
use unicode_segmentation::{Graphemes, UnicodeSegmentation}; use unicode_segmentation::{Graphemes, UnicodeSegmentation};
pub fn is_alphabetic_extended_str(input: &str) -> bool { fn is_word_char_str(input: &str) -> bool {
let x = match input { let x = match input {
value if value.chars().all(|c| ('a'..='z').contains(&c)) => true, "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L"
value if value.chars().all(|c| ('A'..='Z').contains(&c)) => true, | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X"
"Ω" | "" | "µ" | "μ" | "π" => true, | "Y" | "Z" => true,
"a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l"
| "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x"
| "y" | "z" => true,
"Ω" | "" | "µ" | "μ" => true,
_ => false, _ => false,
}; };
return x; return x;
} }
pub fn is_numeric_str(input: &str) -> bool { fn is_numeric_str(input: &str) -> bool {
match input { match input {
"." => true, "." => true,
"0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" => true, "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" => true,
@ -33,10 +37,10 @@ pub fn is_numeric_str(input: &str) -> bool {
/// Read next characters as a word, otherwise return empty string. /// Read next characters as a word, otherwise return empty string.
/// Returns an empty string if there's leading whitespace. /// Returns an empty string if there's leading whitespace.
pub fn read_word_plain(chars: &mut Peekable<Graphemes>) -> String { fn read_word_plain(chars: &mut Peekable<Graphemes>) -> String {
let mut word = String::new(); let mut word = String::new();
while let Some(next_char) = chars.peek() { while let Some(next_char) = chars.peek() {
if is_alphabetic_extended_str(&next_char) { if is_word_char_str(&next_char) {
word += chars.next().unwrap(); word += chars.next().unwrap();
} else { } else {
break; break;
@ -47,7 +51,7 @@ pub fn read_word_plain(chars: &mut Peekable<Graphemes>) -> String {
/// Read next as a word, otherwise return empty string. /// Read next as a word, otherwise return empty string.
/// Leading whitespace is ignored. A trailing digit may be included. /// Leading whitespace is ignored. A trailing digit may be included.
pub fn read_word(first_c: &str, lexer: &mut Lexer) -> String { fn read_word(first_c: &str, lexer: &mut Lexer) -> String {
let chars = &mut lexer.chars; let chars = &mut lexer.chars;
let mut word = first_c.trim().to_owned(); let mut word = first_c.trim().to_owned();
if word == "" { if word == "" {
@ -61,12 +65,13 @@ pub fn read_word(first_c: &str, lexer: &mut Lexer) -> String {
} }
} }
while let Some(next_char) = chars.peek() { while let Some(next_char) = chars.peek() {
if is_alphabetic_extended_str(&next_char) { if is_word_char_str(&next_char) {
word += chars.next().unwrap(); word += chars.next().unwrap();
} else { } else {
break; break;
} }
} }
if word != "" {
match *chars.peek().unwrap_or(&"") { match *chars.peek().unwrap_or(&"") {
"2" | "²" => { "2" | "²" => {
word += "2"; word += "2";
@ -78,14 +83,15 @@ pub fn read_word(first_c: &str, lexer: &mut Lexer) -> String {
}, },
_ => {}, _ => {},
} }
}
return word; return word;
} }
pub fn parse_token(c: &str, lexer: &mut Lexer) -> Result<(), String> { fn parse_token(c: &str, lexer: &mut Lexer) -> Result<(), String> {
let tokens = &mut lexer.tokens; let tokens = &mut lexer.tokens;
match c { match c {
value if value.trim().is_empty() => {}, value if value.trim().is_empty() => {},
value if is_alphabetic_extended_str(&value) => { value if is_word_char_str(&value) => {
parse_word(read_word(c, lexer).as_str(), lexer)?; parse_word(read_word(c, lexer).as_str(), lexer)?;
}, },
value if is_numeric_str(value) => { value if is_numeric_str(value) => {
@ -138,7 +144,15 @@ pub fn parse_token(c: &str, lexer: &mut Lexer) -> Result<(), String> {
Ok(()) Ok(())
} }
pub fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> { fn parse_word_if_non_empty(word: &str, lexer: &mut Lexer) -> Result<(), String> {
match word {
"" => Ok(()),
_ => parse_word(word, lexer)
}
}
fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
println!("word {}", word);
let token = match word { let token = match word {
"to" => Token::TextOperator(To), "to" => Token::TextOperator(To),
"of" => Token::TextOperator(Of), "of" => Token::TextOperator(Of),
@ -337,7 +351,7 @@ pub fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
other => { other => {
lexer.tokens.push(Token::Unit(Pound)); lexer.tokens.push(Token::Unit(Pound));
lexer.tokens.push(Token::Operator(Minus)); lexer.tokens.push(Token::Operator(Minus));
parse_token(&other, lexer)?; parse_word_if_non_empty(&other, lexer)?;
return Ok(()); return Ok(());
} }
} }
@ -464,7 +478,7 @@ pub fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
"hr" | "hrs" | "hour" | "hours" => Token::Unit(WattHour), "hr" | "hrs" | "hour" | "hours" => Token::Unit(WattHour),
other => { other => {
lexer.tokens.push(Token::Unit(Watt)); lexer.tokens.push(Token::Unit(Watt));
parse_token(other, lexer)?; parse_word_if_non_empty(other, lexer)?;
return Ok(()); return Ok(());
}, },
} }
@ -474,7 +488,7 @@ pub fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
"hr" | "hrs" | "hour" | "hours" => Token::Unit(KilowattHour), "hr" | "hrs" | "hour" | "hours" => Token::Unit(KilowattHour),
other => { other => {
lexer.tokens.push(Token::Unit(Kilowatt)); lexer.tokens.push(Token::Unit(Kilowatt));
parse_token(other, lexer)?; parse_word_if_non_empty(other, lexer)?;
return Ok(()); return Ok(());
}, },
} }
@ -484,7 +498,7 @@ pub fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
"hr" | "hrs" | "hour" | "hours" => Token::Unit(MegawattHour), "hr" | "hrs" | "hour" | "hours" => Token::Unit(MegawattHour),
other => { other => {
lexer.tokens.push(Token::Unit(Megawatt)); lexer.tokens.push(Token::Unit(Megawatt));
parse_token(other, lexer)?; parse_word_if_non_empty(other, lexer)?;
return Ok(()); return Ok(());
}, },
} }
@ -494,7 +508,7 @@ pub fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
"hr" | "hrs" | "hour" | "hours" => Token::Unit(GigawattHour), "hr" | "hrs" | "hour" | "hours" => Token::Unit(GigawattHour),
other => { other => {
lexer.tokens.push(Token::Unit(Gigawatt)); lexer.tokens.push(Token::Unit(Gigawatt));
parse_token(other, lexer)?; parse_word_if_non_empty(other, lexer)?;
return Ok(()); return Ok(());
}, },
} }
@ -504,7 +518,7 @@ pub fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
"hr" | "hrs" | "hour" | "hours" => Token::Unit(TerawattHour), "hr" | "hrs" | "hour" | "hours" => Token::Unit(TerawattHour),
other => { other => {
lexer.tokens.push(Token::Unit(Terawatt)); lexer.tokens.push(Token::Unit(Terawatt));
parse_token(other, lexer)?; parse_word_if_non_empty(other, lexer)?;
return Ok(()); return Ok(());
}, },
} }
@ -514,7 +528,7 @@ pub fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
"hr" | "hrs" | "hour" | "hours" => Token::Unit(PetawattHour), "hr" | "hrs" | "hour" | "hours" => Token::Unit(PetawattHour),
other => { other => {
lexer.tokens.push(Token::Unit(Petawatt)); lexer.tokens.push(Token::Unit(Petawatt));
parse_token(other, lexer)?; parse_word_if_non_empty(other, lexer)?;
return Ok(()); return Ok(());
}, },
} }
@ -574,7 +588,7 @@ pub fn parse_word(word: &str, lexer: &mut Lexer) -> Result<(), String> {
return Ok(()); return Ok(());
} }
pub struct Lexer<'a> { struct Lexer<'a> {
left_paren_count: u16, left_paren_count: u16,
right_paren_count: u16, right_paren_count: u16,
chars: Peekable<Graphemes<'a>>, chars: Peekable<Graphemes<'a>>,

View File

@ -8,7 +8,7 @@
//! //!
//! # Example usage //! # Example usage
//! ```rust //! ```rust
//! use cpc::{eval}; //! use cpc::eval;
//! use cpc::units::Unit; //! use cpc::units::Unit;
//! //!
//! match eval("3m + 1cm", true, Unit::Celsius, false) { //! match eval("3m + 1cm", true, Unit::Celsius, false) {
@ -214,7 +214,7 @@ macro_rules! numtok {
/// ///
/// Example: /// Example:
/// ```rust /// ```rust
/// use cpc::{eval}; /// use cpc::eval;
/// use cpc::units::Unit; /// use cpc::units::Unit;
/// ///
/// match eval("3m + 1cm", true, Unit::Celsius, false) { /// match eval("3m + 1cm", true, Unit::Celsius, false) {
@ -230,12 +230,14 @@ macro_rules! numtok {
pub fn eval(input: &str, allow_trailing_operators: bool, default_degree: Unit, verbose: bool) -> Result<Number, String> { pub fn eval(input: &str, allow_trailing_operators: bool, default_degree: Unit, verbose: bool) -> Result<Number, String> {
let lex_start = Instant::now(); let lex_start = Instant::now();
println!("lex");
match lexer::lex(input, allow_trailing_operators, default_degree) { match lexer::lex(input, allow_trailing_operators, default_degree) {
Ok(tokens) => { Ok(tokens) => {
let lex_time = Instant::now().duration_since(lex_start).as_nanos() as f32; let lex_time = Instant::now().duration_since(lex_start).as_nanos() as f32;
if verbose == true { println!("Lexed TokenVector: {:?}", tokens); } if verbose == true { println!("Lexed TokenVector: {:?}", tokens); }
println!("parse");
let parse_start = Instant::now(); let parse_start = Instant::now();
match parser::parse(&tokens) { match parser::parse(&tokens) {
Ok(ast) => { Ok(ast) => {
@ -243,6 +245,7 @@ pub fn eval(input: &str, allow_trailing_operators: bool, default_degree: Unit, v
if verbose == true { println!("Parsed AstNode: {:#?}", ast); } if verbose == true { println!("Parsed AstNode: {:#?}", ast); }
let eval_start = Instant::now(); let eval_start = Instant::now();
println!("eval");
match evaluator::evaluate(&ast) { match evaluator::evaluate(&ast) {
Ok(answer) => { Ok(answer) => {
let eval_time = Instant::now().duration_since(eval_start).as_nanos() as f32; let eval_time = Instant::now().duration_since(eval_start).as_nanos() as f32;