diff --git a/src/vm/analysis/type_checker/natives/sequences.rs b/src/vm/analysis/type_checker/natives/sequences.rs index 99419da0b6..f340223d8d 100644 --- a/src/vm/analysis/type_checker/natives/sequences.rs +++ b/src/vm/analysis/type_checker/natives/sequences.rs @@ -405,60 +405,31 @@ pub fn check_special_slice( ) -> TypeResult { check_argument_count(3, args)?; - // Position - let position = match args[1].expr { - SymbolicExpressionType::LiteralValue(Value::UInt(position)) => position, - _ => { - let position_type = checker.type_check(&args[1], context)?; - return Err(CheckErrors::TypeError(TypeSignature::UIntType, position_type).into()); - } - }; - checker - .type_map - .set_type(&args[1], TypeSignature::UIntType)?; - - let position = u32::try_from(position).map_err(|_e| CheckErrors::MaxLengthOverflow)?; - - // Length - let length = match args[2].expr { - SymbolicExpressionType::LiteralValue(Value::UInt(expected_len)) => expected_len, - _ => { - let expected_len_type = checker.type_check(&args[2], context)?; - return Err(CheckErrors::TypeError(TypeSignature::UIntType, expected_len_type).into()); - } - }; - checker - .type_map - .set_type(&args[2], TypeSignature::UIntType)?; - - let length = u32::try_from(length).map_err(|_e| CheckErrors::MaxLengthOverflow)?; - - // Sequence + // Check sequence let seq_type = checker.type_check(&args[0], context)?; - - let (origin_len, resized_seq) = match &seq_type { - TypeSignature::SequenceType(ListType(list)) => ( - list.get_max_len(), - TypeSignature::list_of(list.get_list_item_type().clone(), length)?, - ), - TypeSignature::SequenceType(BufferType(len)) => ( - len.0, - TypeSignature::SequenceType(BufferType(BufferLength(length))), - ), - TypeSignature::SequenceType(StringType(ASCII(len))) => ( - len.0, - TypeSignature::SequenceType(StringType(ASCII(BufferLength(length)))), - ), - TypeSignature::SequenceType(StringType(UTF8(len))) => ( - len.0, - TypeSignature::SequenceType(StringType(UTF8(StringUTF8Length(length)))), - ), + let seq = match &seq_type { + TypeSignature::SequenceType(seq) => TypeSignature::SequenceType(seq.clone()), _ => return Err(CheckErrors::ExpectedSequence(seq_type.clone()).into()), }; + // TODO: runtime_cost - if (position + length) > origin_len { - return TypeSignature::new_option(TypeSignature::NoType).map_err(|e| e.into()); - } + // Check position argument + let position = checker.type_check(&args[1], context)?; + match position { + TypeSignature::UIntType => Ok(()), + _ => Err(CheckErrors::TypeError(TypeSignature::UIntType, position)), + }?; + // TODO: runtime_cost + + // Check length argument + let length = checker.type_check(&args[2], context)?; + match length { + TypeSignature::UIntType => Ok(()), + _ => Err(CheckErrors::TypeError(TypeSignature::UIntType, length)), + }?; + // TODO: runtime_cost + + // TODO: analysis_typecheck_cost - Ok(resized_seq) + Ok(seq) } diff --git a/src/vm/analysis/type_checker/tests/mod.rs b/src/vm/analysis/type_checker/tests/mod.rs index 566ea8f975..980d8a224c 100644 --- a/src/vm/analysis/type_checker/tests/mod.rs +++ b/src/vm/analysis/type_checker/tests/mod.rs @@ -1081,8 +1081,17 @@ fn test_slice_list() { let good = [ "(slice (list 2 3 4 5 6 7 8) u0 u3)", "(slice (list u0 u1 u2 u3 u4) u3 u2)", + "(slice (list 2 3 4 5 6 7 8) u0 u0)", + "(slice (list 2 3 4 5 6 7 8) u10 u3)", + "(slice (list) u0 u3)", + ]; + let expected = [ + "(list 7 int)", + "(list 5 uint)", + "(list 7 int)", + "(list 7 int)", + "(list 0 UnknownType)", ]; - let expected = ["(list 3 int)", "(list 2 uint)"]; for (good_test, expected) in good.iter().zip(expected.iter()) { assert_eq!( @@ -1113,7 +1122,7 @@ fn test_slice_buff() { "(slice 0x000102030405 u0 u3)", "(slice 0x000102030405 u3 u2)", ]; - let expected = ["(buff 3)", "(buff 2)"]; + let expected = ["(buff 6)", "(buff 6)"]; for (good_test, expected) in good.iter().zip(expected.iter()) { assert_eq!( @@ -1144,7 +1153,7 @@ fn test_slice_ascii() { "(slice \"blockstack\" u4 u5)", "(slice \"blockstack\" u0 u5)", ]; - let expected = ["(string-ascii 5)", "(string-ascii 5)"]; + let expected = ["(string-ascii 10)", "(string-ascii 10)"]; for (good_test, expected) in good.iter().zip(expected.iter()) { assert_eq!( @@ -1175,7 +1184,7 @@ fn test_slice_utf8() { "(slice u\"blockstack\" u4 u5)", "(slice u\"blockstack\" u4 u5)", ]; - let expected = ["(string-utf8 5)", "(string-utf8 5)"]; + let expected = ["(string-utf8 10)", "(string-utf8 10)"]; for (good_test, expected) in good.iter().zip(expected.iter()) { assert_eq!( diff --git a/src/vm/docs/mod.rs b/src/vm/docs/mod.rs index 06e24f248e..1730759aa5 100644 --- a/src/vm/docs/mod.rs +++ b/src/vm/docs/mod.rs @@ -822,10 +822,10 @@ const SLICE_API: SpecialAPI = SpecialAPI { description: "The `slice` function returns a sub-sequence of size `length` in the provided sequence. If `length` is 0 or `position + length` is greater than or equal to `(len sequence)`, this function returns `none`.", - example: "(slice \"blockstack\" u5 u5) ;; Returns (some \"stack\") -(slice (list 1 2 3 4 5) u5 u2) ;; Returns none -(slice (list 1 2 3 4 5) u3 u1) ;; Returns (some (4)) -(slice \"abcd\" u1 u2) ;; Returns (some \"bc\") + example: "(slice \"blockstack\" u5 u5) ;; Returns \"stack\" +(slice (list 1 2 3 4 5) u5 u2) ;; Returns () +(slice (list 1 2 3 4 5) u3 u1) ;; Returns (4) +(slice \"abcd\" u1 u2) ;; Returns \"bc\" ", }; diff --git a/src/vm/functions/sequences.rs b/src/vm/functions/sequences.rs index 714e2fbe41..61bc1959e2 100644 --- a/src/vm/functions/sequences.rs +++ b/src/vm/functions/sequences.rs @@ -319,11 +319,7 @@ pub fn special_slice( ) -> Result { check_argument_count(3, args)?; - runtime_cost( - ClarityCostFunction::Concat, - env, - 0, - )?; + // TODO: runtime_cost let seq = eval(&args[0], env, context)?; let position = eval(&args[1], env, context)?; @@ -336,12 +332,9 @@ pub fn special_slice( _ => return Ok(Value::none()), }; - match seq.slice(position, length) { - Ok(v) => Value::some(v)?, - Err(_) => Value::none(), - } + seq.slice(position, length) } _ => return Err(RuntimeErrorType::BadTypeConstruction.into()), - }; + }?; Ok(sliced_seq) } diff --git a/src/vm/tests/sequences.rs b/src/vm/tests/sequences.rs index 8914725cb1..1c16715335 100644 --- a/src/vm/tests/sequences.rs +++ b/src/vm/tests/sequences.rs @@ -453,137 +453,90 @@ fn test_slice_list() { let tests = [ "(slice (list 2 3 4 5 6 7 8) u0 u3)", "(slice (list u0 u1 u2 u3 u4) u3 u2)", - // "(append (append (list) 1) 2)", + "(slice (list 2 3 4 5 6 7 8) u0 u0)", + "(slice (list 2 3 4 5 6 7 8) u10 u3)", ]; let expected = [ - Value::some(Value::list_from(vec![Value::Int(2), Value::Int(3), Value::Int(4)]).unwrap()) - .unwrap(), - Value::some(Value::list_from(vec![Value::UInt(3), Value::UInt(4)]).unwrap()).unwrap(), - // Value::list_from(vec![Value::Int(1), Value::Int2)]).unwrap(), + Value::list_from(vec![Value::Int(2), Value::Int(3), Value::Int(4)]).unwrap(), + Value::list_from(vec![Value::UInt(3), Value::UInt(4)]).unwrap(), + Value::list_from(vec![]).unwrap(), + Value::list_from(vec![]).unwrap(), + ]; + + for (test, expected) in tests.iter().zip(expected.iter()) { + assert_eq!(expected.clone(), execute(test).unwrap().unwrap()); + } +} + +#[test] +fn test_slice_buff() { + let tests = [ + "(slice 0x000102030405 u0 u3)", + "(slice 0x000102030405 u3 u3)", + "(slice 0x000102030405 u3 u10)", + "(slice 0x000102030405 u10 u3)", + "(slice 0x u2 u3)", + ]; + + let expected = [ + Value::buff_from(vec![0, 1, 2]).unwrap(), + Value::buff_from(vec![3, 4, 5]).unwrap(), + Value::buff_from(vec![]).unwrap(), + Value::buff_from(vec![]).unwrap(), + Value::buff_from(vec![]).unwrap(), + ]; + + for (test, expected) in tests.iter().zip(expected.iter()) { + assert_eq!(expected.clone(), execute(test).unwrap().unwrap()); + } +} + +#[test] +fn test_slice_ascii() { + let tests = [ + "(slice \"blockstack\" u0 u5)", + "(slice \"blockstack\" u5 u5)", + "(slice \"blockstack\" u5 u0)", + "(slice \"blockstack\" u11 u3)", + "(slice \"\" u0 u3)", + ]; + + let expected = [ + Value::string_ascii_from_bytes("block".into()).unwrap(), + Value::string_ascii_from_bytes("stack".into()).unwrap(), + Value::string_ascii_from_bytes(vec![]).unwrap(), + Value::string_ascii_from_bytes(vec![]).unwrap(), + Value::string_ascii_from_bytes(vec![]).unwrap(), ]; for (test, expected) in tests.iter().zip(expected.iter()) { assert_eq!(expected.clone(), execute(test).unwrap().unwrap()); } +} + +#[test] +fn test_slice_utf8() { + let tests = [ + "(slice u\"hello \\u{1F98A}\" u0 u5)", + "(slice u\"hello \\u{1F98A}\" u6 u1)", + "(slice u\"hello \\u{1F98A}\" u6 u0)", + "(slice u\"hello \\u{1F98A}\" u11 u4)", + "(slice u\"\" u0 u3)", + ]; + + let expected = [ + Value::string_utf8_from_bytes("hello".into()).unwrap(), + Value::string_utf8_from_bytes("🦊".into()).unwrap(), + Value::string_utf8_from_bytes(vec![]).unwrap(), + Value::string_utf8_from_bytes(vec![]).unwrap(), + Value::string_utf8_from_bytes(vec![]).unwrap(), + ]; - // assert_eq!( - // execute("(append (append (list) 1) u2)").unwrap_err(), - // CheckErrors::TypeValueError(IntType, Value::UInt(2)).into() - // ); -} - -// #[test] -// fn test_slice_list() { -// let good = ["(slice (list 2 3 4 5 6 7 8) u0 u3)", "(slice (list u0 u1 u2 u3 u4) u3 u2)"]; -// let expected = ["(list 3 int)", "(list 2 uint)"]; - -// for (good_test, expected) in good.iter().zip(expected.iter()) { -// assert_eq!( -// expected, -// &format!("{}", type_check_helper(&good_test).unwrap()) -// ); -// } - -// let bad = [ -// "(slice (list 2 3) 3 u4)", -// "(slice (list 2 3) u3 4)", -// "(slice (list u0) u1)", -// ]; - -// let bad_expected = [ -// CheckErrors::TypeError(UIntType, IntType), -// CheckErrors::TypeError(UIntType, IntType), -// CheckErrors::IncorrectArgumentCount(3, 2), -// ]; -// for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { -// assert_eq!(expected, &type_check_helper(&bad_test).unwrap_err().err); -// } -// } - -// #[test] -// fn test_slice_buff() { -// let good = ["(slice 0x000102030405 u0 u3)", "(slice 0x000102030405 u3 u2)"]; -// let expected = ["(buff 3)", "(buff 2)"]; - -// for (good_test, expected) in good.iter().zip(expected.iter()) { -// assert_eq!( -// expected, -// &format!("{}", type_check_helper(&good_test).unwrap()) -// ); -// } - -// let bad = [ -// "(slice 0x000102030405 3 u4)", -// "(slice 0x000102030405 u3 4)", -// "(slice 0x000102030405 u1)", -// ]; - -// let bad_expected = [ -// CheckErrors::TypeError(UIntType, IntType), -// CheckErrors::TypeError(UIntType, IntType), -// CheckErrors::IncorrectArgumentCount(3, 2), -// ]; -// for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { -// assert_eq!(expected, &type_check_helper(&bad_test).unwrap_err().err); -// } -// } - -// #[test] -// fn test_slice_ascii() { -// let good = ["(slice \"blockstack\" u4 u5)", "(slice \"blockstack\" u0 u5)"]; -// let expected = ["(string-ascii 5)", "(string-ascii 5)"]; - -// for (good_test, expected) in good.iter().zip(expected.iter()) { -// assert_eq!( -// expected, -// &format!("{}", type_check_helper(&good_test).unwrap()) -// ); -// } - -// let bad = [ -// "(slice \"blockstack\" 3 u4)", -// "(slice \"blockstack\" u3 4)", -// "(slice \"blockstack\" u1)", -// ]; - -// let bad_expected = [ -// CheckErrors::TypeError(UIntType, IntType), -// CheckErrors::TypeError(UIntType, IntType), -// CheckErrors::IncorrectArgumentCount(3, 2), -// ]; -// for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { -// assert_eq!(expected, &type_check_helper(&bad_test).unwrap_err().err); -// } -// } - -// #[test] -// fn test_slice_utf8() { -// let good = ["(slice u\"blockstack\" u4 u5)", "(slice u\"blockstack\" u4 u5)"]; -// let expected = ["(string-utf8 5)", "(string-utf8 5)"]; - -// for (good_test, expected) in good.iter().zip(expected.iter()) { -// assert_eq!( -// expected, -// &format!("{}", type_check_helper(&good_test).unwrap()) -// ); -// } - -// let bad = [ -// "(slice u\"blockstack\" 3 u4)", -// "(slice u\"blockstack\" u3 4)", -// "(slice u\"blockstack\" u1)", -// ]; - -// let bad_expected = [ -// CheckErrors::TypeError(UIntType, IntType), -// CheckErrors::TypeError(UIntType, IntType), -// CheckErrors::IncorrectArgumentCount(3, 2), -// ]; -// for (bad_test, expected) in bad.iter().zip(bad_expected.iter()) { -// assert_eq!(expected, &type_check_helper(&bad_test).unwrap_err().err); -// } -// } + for (test, expected) in tests.iter().zip(expected.iter()) { + assert_eq!(expected.clone(), execute(test).unwrap().unwrap()); + } +} #[test] fn test_simple_list_concat() { diff --git a/src/vm/types/mod.rs b/src/vm/types/mod.rs index 532accd17e..01ea39bce0 100644 --- a/src/vm/types/mod.rs +++ b/src/vm/types/mod.rs @@ -399,24 +399,43 @@ impl SequenceData { } pub fn slice(self, position: usize, length: usize) -> Result { - if position + length > self.len() { - return Err(RuntimeErrorType::BadTypeConstruction.into()); - } + let empty_seq = position + length > self.len(); + let result = match self { SequenceData::Buffer(data) => { - Value::buff_from(data.data[position..(length + position)].to_vec()) + let data = if empty_seq { + vec![] + } else { + data.data[position..(length + position)].to_vec() + }; + Value::buff_from(data) } SequenceData::List(data) => { - Value::list_from(data.data[position..(length + position)].to_vec()) + let data = if empty_seq { + vec![] + } else { + data.data[position..(length + position)].to_vec() + }; + Value::list_from(data) } SequenceData::String(CharType::ASCII(data)) => { - Value::string_ascii_from_bytes(data.data[position..(length + position)].to_vec()) + let data = if empty_seq { + vec![] + } else { + data.data[position..(length + position)].to_vec() + }; + Value::string_ascii_from_bytes(data) + } + SequenceData::String(CharType::UTF8(data)) => { + let data = if empty_seq { + vec![] + } else { + data.data[position..(length + position)].to_vec() + }; + Ok(Value::Sequence(SequenceData::String(CharType::UTF8( + UTF8Data { data }, + )))) } - SequenceData::String(CharType::UTF8(data)) => Ok(Value::Sequence( - SequenceData::String(CharType::UTF8(UTF8Data { - data: data.data[position..(length + position)].to_vec(), - })), - )), }?; Ok(result)