diff --git a/src/expr/expr.rs b/src/expr/expr.rs index 63fb67d2b..d6a7664d2 100644 --- a/src/expr/expr.rs +++ b/src/expr/expr.rs @@ -105,9 +105,9 @@ separates increasing precedence groups. EXPRESSION may be: STRING : REGEXP [NOT IMPLEMENTED] anchored pattern match of REGEXP in STRING match STRING REGEXP [NOT IMPLEMENTED] same as STRING : REGEXP - substr STRING POS LENGTH [NOT IMPLEMENTED] substring of STRING, POS counted from 1 - index STRING CHARS [NOT IMPLEMENTED] index in STRING where any CHARS is found, or 0 - length STRING [NOT IMPLEMENTED] length of STRING + substr STRING POS LENGTH substring of STRING, POS counted from 1 + index STRING CHARS index in STRING where any CHARS is found, or 0 + length STRING length of STRING + TOKEN interpret TOKEN as a string, even if it is a keyword like 'match' or an operator like '/' diff --git a/src/expr/syntax_tree.rs b/src/expr/syntax_tree.rs index 253ace0de..53819974d 100644 --- a/src/expr/syntax_tree.rs +++ b/src/expr/syntax_tree.rs @@ -103,6 +103,9 @@ impl ASTNode { |a: &String, b: &String| Ok( bool_as_string(a >= b) ), &operand_values ), + "length" => prefix_operator_length( &operand_values ), + "index" => prefix_operator_index( &operand_values ), + "substr" => prefix_operator_substr( &operand_values ), _ => Err(format!("operation not implemented: {}", op_type)) } @@ -328,6 +331,55 @@ fn infix_operator_two_ints_or_two_strings( fi: FI, fs: FS, values: &Vec< } } +fn prefix_operator_length( values: &Vec ) -> Result { + assert!( values.len() == 1 ); + Ok( values[0].len().to_string() ) +} + +fn prefix_operator_index( values: &Vec ) -> Result { + assert!( values.len() == 2 ); + let haystack = &values[0]; + let needles = &values[1]; + + let mut current_idx = 0; + for ch_h in haystack.chars() { + current_idx += 1; + + for ch_n in needles.chars() { + if ch_n == ch_h { + return Ok( current_idx.to_string() ) + } + } + } + Ok( "0".to_string() ) +} + +fn prefix_operator_substr( values: &Vec ) -> Result { + assert!( values.len() == 3 ); + let subj = &values[0]; + let mut idx = match values[1].parse::() { + Ok( i ) => i, + Err( _ ) => return Err( "expected integer as POS arg to 'substr'".to_string() ), + }; + let mut len = match values[2].parse::() { + Ok( i ) => i, + Err( _ ) => return Err( "expected integer as LENGTH arg to 'substr'".to_string() ), + }; + + if idx <= 0 || len <= 0 { return Ok( "".to_string() ) } + + let mut out_str = String::new(); + for ch in subj.chars() { + idx -= 1; + if idx <= 0 { + if len <= 0 { break; } + len -= 1; + + out_str.push( ch ); + } + } + Ok( out_str ) +} fn bool_as_int( b: bool ) -> i64 { if b { 1 } else { 0 } } fn bool_as_string( b: bool ) -> String { if b { "1".to_string() } else { "0".to_string() } } diff --git a/src/expr/tokens.rs b/src/expr/tokens.rs index 0bae1066a..f1c0a65bf 100644 --- a/src/expr/tokens.rs +++ b/src/expr/tokens.rs @@ -110,6 +110,7 @@ pub fn strings_to_tokens( strings: &Vec ) -> Result< Vec<(usize, Token)> "match" => Token::PrefixOp{ arity: 2, value: s.clone() }, "substr" => Token::PrefixOp{ arity: 3, value: s.clone() }, "index" => Token::PrefixOp{ arity: 2, value: s.clone() }, + "length" => Token::PrefixOp{ arity: 1, value: s.clone() }, _ => Token::new_value( &s ), };