difftreelog
Merge pull request #89 from CertainLach/parse-intrinsics
in: master
2 files changed
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth128 ("asciiUpper", builtin_ascii_upper::INST),128 ("asciiUpper", builtin_ascii_upper::INST),129 ("asciiLower", builtin_ascii_lower::INST),129 ("asciiLower", builtin_ascii_lower::INST),130 ("findSubstr", builtin_find_substr::INST),130 ("findSubstr", builtin_find_substr::INST),131 ("parseInt", builtin_parse_int::INST),132 ("parseOctal", builtin_parse_octal::INST),133 ("parseHex", builtin_parse_hex::INST),131 // Misc134 // Misc132 ("length", builtin_length::INST),135 ("length", builtin_length::INST),133 ("startsWith", builtin_starts_with::INST),136 ("startsWith", builtin_starts_with::INST),312 out.build()315 out.build()313 }316 }314 #[cfg(feature = "legacy-this-file")]317 #[cfg(feature = "legacy-this-file")]315 fn initialize(&self, s: State, source: Source) -> jrsonnet_evaluator::Context {318 fn initialize(&self, s: State, source: Source) -> Context {316 let mut builder = ObjValueBuilder::new();319 let mut builder = ObjValueBuilder::new();317 builder.with_super(self.stdlib_obj.clone());320 builder.with_super(self.stdlib_obj.clone());318 builder321 buildercrates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth1use jrsonnet_evaluator::{1use jrsonnet_evaluator::{2 error::{ErrorKind::*, Result},2 error::{ErrorKind::*, Result},3 function::builtin,3 function::builtin,4 throw,4 typed::{Either2, VecVal, M1},5 typed::{Either2, VecVal, M1},5 val::ArrValue,6 val::ArrValue,6 Either, IStr, Val,7 Either, IStr, Val,74 Ok(out.into())75 Ok(out.into())75}76}7778#[builtin]79pub fn builtin_parse_int(raw: IStr) -> Result<f64> {80 if let Some(raw) = raw.strip_prefix('-') {81 if raw.is_empty() {82 throw!("integer only consists of a minus")83 }8485 parse_nat::<10>(raw).map(|value| -value)86 } else {87 if raw.is_empty() {88 throw!("empty integer")89 }9091 parse_nat::<10>(raw.as_str())92 }93}9495#[builtin]96pub fn builtin_parse_octal(raw: IStr) -> Result<f64> {97 if raw.is_empty() {98 throw!("empty octal integer");99 }100101 parse_nat::<8>(raw.as_str())102}103104#[builtin]105pub fn builtin_parse_hex(raw: IStr) -> Result<f64> {106 if raw.is_empty() {107 throw!("empty hexadecimal integer");108 }109110 parse_nat::<16>(raw.as_str())111}112113fn parse_nat<const BASE: u32>(raw: &str) -> Result<f64> {114 debug_assert!(115 1 <= BASE && BASE <= 16,116 "integer base should be between 1 and 16"117 );118119 const ZERO_CODE: u32 = '0' as u32;120 const UPPER_A_CODE: u32 = 'A' as u32;121 const LOWER_A_CODE: u32 = 'a' as u32;122123 #[inline]124 fn checked_sub_if(condition: bool, lhs: u32, rhs: u32) -> Option<u32> {125 if condition {126 lhs.checked_sub(rhs)127 } else {128 None129 }130 }131132 let base = BASE as f64;133134 raw.chars().try_fold(0f64, |aggregate, digit| {135 let digit = digit as u32;136 let digit = if let Some(digit) = checked_sub_if(BASE > 10, digit, LOWER_A_CODE) {137 digit + 10138 } else if let Some(digit) = checked_sub_if(BASE > 10, digit, UPPER_A_CODE) {139 digit + 10140 } else {141 digit.checked_sub(ZERO_CODE).unwrap_or(BASE)142 };143144 if digit < BASE {145 Ok(base * aggregate + digit as f64)146 } else {147 throw!("{raw:?} is not a base {BASE} integer",);148 }149 })150}151152#[cfg(test)]153mod tests {154 use super::*;155156 #[test]157 fn parse_nat_base_8() {158 assert_eq!(parse_nat::<8>("0").unwrap(), 0.);159 assert_eq!(parse_nat::<8>("5").unwrap(), 5.);160 assert_eq!(parse_nat::<8>("32").unwrap(), 0o32 as f64);161 assert_eq!(parse_nat::<8>("761").unwrap(), 0o761 as f64);162 }163164 #[test]165 fn parse_nat_base_10() {166 assert_eq!(parse_nat::<10>("0").unwrap(), 0.);167 assert_eq!(parse_nat::<10>("3").unwrap(), 3.);168 assert_eq!(parse_nat::<10>("27").unwrap(), 27.);169 assert_eq!(parse_nat::<10>("123").unwrap(), 123.);170 }171172 #[test]173 fn parse_nat_base_16() {174 assert_eq!(parse_nat::<16>("0").unwrap(), 0.);175 assert_eq!(parse_nat::<16>("A").unwrap(), 10.);176 assert_eq!(parse_nat::<16>("a9").unwrap(), 0xA9 as f64);177 assert_eq!(parse_nat::<16>("BbC").unwrap(), 0xBBC as f64);178 }179}76180