diff --git a/crates/wasm-wave/src/ast.rs b/crates/wasm-wave/src/ast.rs index 91d989d7f0..74c4348554 100644 --- a/crates/wasm-wave/src/ast.rs +++ b/crates/wasm-wave/src/ast.rs @@ -227,6 +227,7 @@ impl Node { WasmTypeKind::Option => self.to_wasm_option(ty, src)?, WasmTypeKind::Result => self.to_wasm_result(ty, src)?, WasmTypeKind::Flags => self.to_wasm_flags(ty, src)?, + WasmTypeKind::Handle => self.to_wasm_handle(ty, src)?, other => { return Err( self.wasm_value_error(WasmValueError::UnsupportedType(other.to_string())) @@ -352,6 +353,17 @@ impl Node { V::make_option(ty, value).map_err(|err| self.wasm_value_error(err)) } + fn to_wasm_handle(&self, _ty: &V::Type, src: &str) -> Result { + self.ensure_type(NodeType::Handle)?; + match self.children.as_slice() { + [label] => { + let label = label.as_label(src)?; + Ok(V::make_handle(label.into())) + } + _ => Err(self.error(ParserErrorKind::InvalidType)), + } + } + fn to_wasm_result(&self, ty: &V::Type, src: &str) -> Result { let (ok_type, err_type) = ty.result_types().unwrap(); let value = match self.ty { @@ -470,4 +482,7 @@ pub enum NodeType { /// Flags /// Child nodes are flag Labels. Flags, + /// Handle + /// Child node is a opaque label. + Handle, } diff --git a/crates/wasm-wave/src/lex.rs b/crates/wasm-wave/src/lex.rs index 27d71d9c51..8ae8210803 100644 --- a/crates/wasm-wave/src/lex.rs +++ b/crates/wasm-wave/src/lex.rs @@ -12,7 +12,8 @@ pub type Lexer<'source> = logos::Lexer<'source, Token>; #[logos(error = Option)] #[logos(skip r"[ \t\n\r]+")] #[logos(skip r"//[^\n]*")] -#[logos(subpattern label_word = r"[a-z][a-z0-9]*|[A-Z][A-Z0-9]*")] +#[logos(subpattern first_label_word = r"[a-z][a-z0-9]*|[A-Z][A-Z0-9]*")] +#[logos(subpattern label_word = r"[a-z0-9]+|[A-Z0-9]+")] #[logos(subpattern char_escape = r#"\\['"tnr\\]|\\u\{[0-9a-fA-F]{1,6}\}"#)] pub enum Token { /// The `{` symbol @@ -40,6 +41,10 @@ pub enum Token { #[token(":")] Colon, + /// The `#` symbol + #[token("#")] + Sharp, + /// The `,` symbol #[token(",")] Comma, @@ -50,7 +55,7 @@ pub enum Token { Number, /// A label or keyword - #[regex(r"%?(?&label_word)(-(?&label_word))*")] + #[regex(r"%?(?&first_label_word)(-(?&label_word))*")] LabelOrKeyword, /// A char literal diff --git a/crates/wasm-wave/src/parser.rs b/crates/wasm-wave/src/parser.rs index dd17954b73..e1a80b897d 100644 --- a/crates/wasm-wave/src/parser.rs +++ b/crates/wasm-wave/src/parser.rs @@ -81,6 +81,7 @@ impl<'source> Parser<'source> { Token::ParenOpen => self.parse_tuple()?, Token::BracketOpen => self.parse_list()?, Token::BraceOpen => self.parse_record_or_flags()?, + Token::Sharp => self.parse_handle()?, Token::LabelOrKeyword => match Keyword::decode(self.slice()) { Some(Keyword::True) => self.leaf_node(NodeType::BoolTrue), Some(Keyword::False) => self.leaf_node(NodeType::BoolFalse), @@ -99,6 +100,13 @@ impl<'source> Parser<'source> { }) } + fn parse_handle(&mut self) -> Result { + let start = self.span().start; + self.advance()?; + let label = self.parse_label()?; + Ok(Node::new(NodeType::Handle, start..self.span().end, [label])) + } + fn parse_tuple(&mut self) -> Result { let start = self.span().start; let children = self.parse_comma_separated_nodes(Token::ParenClose)?; @@ -485,6 +493,20 @@ mod tests { ); } + #[test] + fn parse_resource() { + let ty = Type::handle("test_resource"); + assert_eq!( + parse_value("#http-body-42", &ty), + Value::make_handle("http-body-42".into()) + ); + let ty = Type::handle("resource"); + assert_eq!( + parse_value("#borrow-http-body", &ty), + Value::make_handle("borrow-http-body".into()) + ); + } + #[test] fn parse_record_reordering() { let ty = Type::record([("red", Type::S32), ("green", Type::CHAR)]).unwrap(); diff --git a/crates/wasm-wave/src/untyped.rs b/crates/wasm-wave/src/untyped.rs index 8f57eb8747..c7a700e30f 100644 --- a/crates/wasm-wave/src/untyped.rs +++ b/crates/wasm-wave/src/untyped.rs @@ -184,6 +184,7 @@ fn fmt_node(f: &mut impl std::fmt::Write, node: &Node, src: &str) -> std::fmt::R } f.write_char('}') } + Handle => todo!(), } } diff --git a/crates/wasm-wave/src/value/convert.rs b/crates/wasm-wave/src/value/convert.rs index 6338f165e3..81bc38499e 100644 --- a/crates/wasm-wave/src/value/convert.rs +++ b/crates/wasm-wave/src/value/convert.rs @@ -45,12 +45,19 @@ fn from_optional_wasm_type(ty: Option) -> Option> { }) } -trait ValueTyped { +pub trait ValueTyped { fn value_type() -> Type; } +// ToRust is only defined for owned types +pub trait ToRust { + fn to_rust(&self) -> T; +} +pub trait ToValue { + fn to_value(&self) -> Value; +} macro_rules! impl_primitives { - ($Self:ty, $(($case:ident, $ty:ty)),*) => { + ($Self:ty, $(($case:ident, $ty:ty, $unwrap:ident)),*) => { $( impl ValueTyped for $ty { fn value_type() -> Type { @@ -63,24 +70,35 @@ macro_rules! impl_primitives { Self(ValueEnum::$case(value)) } } + + impl ToRust<$ty> for $Self { + fn to_rust(&self) -> $ty { + self.$unwrap() + } + } + impl ToValue for $ty { + fn to_value(&self) -> Value { + Value(ValueEnum::$case(*self)) + } + } )* }; } impl_primitives!( Value, - (Bool, bool), - (S8, i8), - (S16, i16), - (S32, i32), - (S64, i64), - (U8, u8), - (U16, u16), - (U32, u32), - (U64, u64), - (F32, f32), - (F64, f64), - (Char, char) + (Bool, bool, unwrap_bool), + (S8, i8, unwrap_s8), + (S16, i16, unwrap_s16), + (S32, i32, unwrap_s32), + (S64, i64, unwrap_s64), + (U8, u8, unwrap_u8), + (U16, u16, unwrap_u16), + (U32, u32, unwrap_u32), + (U64, u64, unwrap_u64), + (F32, f32, unwrap_f32), + (F64, f64, unwrap_f64), + (Char, char, unwrap_char) ); impl ValueTyped for String { @@ -94,6 +112,16 @@ impl From for Value { Self(ValueEnum::String(value.into())) } } +impl ToRust for Value { + fn to_rust(&self) -> String { + self.unwrap_string().into() + } +} +impl ToValue for String { + fn to_value(&self) -> Value { + Value(ValueEnum::String(self.to_owned().into())) + } +} impl ValueTyped for &str { fn value_type() -> Type { @@ -106,12 +134,22 @@ impl<'a> From<&'a str> for Value { value.to_string().into() } } +impl<'a> ToValue for &'a str { + fn to_value(&self) -> Value { + self.to_string().to_value() + } +} impl ValueTyped for [T; N] { fn value_type() -> Type { Type::list(T::value_type()) } } +impl ValueTyped for [T] { + fn value_type() -> Type { + Type::list(T::value_type()) + } +} impl> From<[T; N]> for Value { fn from(values: [T; N]) -> Self { @@ -120,6 +158,13 @@ impl> From<[T; N]> for Value { Value::make_list(&ty, values).unwrap() } } +impl ToValue for [T] { + fn to_value(&self) -> Value { + let ty = <[T]>::value_type(); + let values = self.iter().map(|x| x.to_value()); + Value::make_list(&ty, values).unwrap() + } +} impl ValueTyped for Vec { fn value_type() -> Type { @@ -134,6 +179,21 @@ impl> From> for Value { Value::make_list(&ty, values).unwrap() } } +impl ToValue for Vec { + fn to_value(&self) -> Value { + let ty = Vec::::value_type(); + let values = self.iter().map(|x| x.to_value()); + Value::make_list(&ty, values).unwrap() + } +} +impl ToRust> for Value +where + Value: ToRust, +{ + fn to_rust(&self) -> Vec { + self.unwrap_list().map(|x| x.to_rust()).collect() + } +} impl ValueTyped for Option { fn value_type() -> Type { @@ -147,7 +207,36 @@ impl> From> for Value { Value::make_option(&ty, value.map(Into::into)).unwrap() } } +impl ToValue for Option { + fn to_value(&self) -> Value { + let ty = Option::::value_type(); + Value::make_option(&ty, self.as_ref().map(|x| x.to_value())).unwrap() + } +} +impl ToRust> for Value +where + Value: ToRust, +{ + fn to_rust(&self) -> Option { + self.unwrap_option().map(|x| x.to_rust()) + } +} +impl ValueTyped for Result<(), ()> { + fn value_type() -> Type { + Type::result(None, None) + } +} +impl ValueTyped for Result { + fn value_type() -> Type { + Type::result(Some(T::value_type()), None) + } +} +impl ValueTyped for Result<(), U> { + fn value_type() -> Type { + Type::result(None, Some(U::value_type())) + } +} impl ValueTyped for Result { fn value_type() -> Type { Type::result(Some(T::value_type()), Some(U::value_type())) @@ -164,6 +253,91 @@ impl, U: ValueTyped + Into> From Value::make_result(&ty, value).unwrap() } } +impl ToValue for Result<(), ()> { + fn to_value(&self) -> Value { + let ty = Result::<(), ()>::value_type(); + let value = match self { + Ok(()) => Ok(None), + Err(()) => Err(None), + }; + Value::make_result(&ty, value).unwrap() + } +} +impl ToValue for Result { + fn to_value(&self) -> Value { + let ty = Result::::value_type(); + let value = match self { + Ok(ok) => Ok(Some(ok.to_value())), + Err(()) => Err(None), + }; + Value::make_result(&ty, value).unwrap() + } +} +impl ToValue for Result<(), U> { + fn to_value(&self) -> Value { + let ty = Result::<(), U>::value_type(); + let value = match self { + Ok(()) => Ok(None), + Err(err) => Err(Some(err.to_value())), + }; + Value::make_result(&ty, value).unwrap() + } +} +impl ToValue for Result { + fn to_value(&self) -> Value { + let ty = Result::::value_type(); + let value = match self { + Ok(ok) => Ok(Some(ok.to_value())), + Err(err) => Err(Some(err.to_value())), + }; + Value::make_result(&ty, value).unwrap() + } +} +impl ToRust> for Value { + fn to_rust(&self) -> Result<(), ()> { + match self.unwrap_result() { + Ok(None) => Ok(()), + Err(None) => Err(()), + _ => unreachable!(), + } + } +} +impl ToRust> for Value +where + Value: ToRust, +{ + fn to_rust(&self) -> Result { + match self.unwrap_result() { + Ok(Some(ok)) => Ok(ok.to_rust()), + Err(None) => Err(()), + _ => unreachable!(), + } + } +} +impl ToRust> for Value +where + Value: ToRust, +{ + fn to_rust(&self) -> Result<(), U> { + match self.unwrap_result() { + Ok(None) => Ok(()), + Err(Some(err)) => Err(err.to_rust()), + _ => unreachable!(), + } + } +} +impl ToRust> for Value +where + Value: ToRust + ToRust, +{ + fn to_rust(&self) -> Result { + match self.unwrap_result() { + Ok(Some(ok)) => Ok(ok.to_rust()), + Err(Some(err)) => Err(err.to_rust()), + _ => unreachable!(), + } + } +} macro_rules! impl_tuple { ($(($($var:ident),*)),*) => { @@ -184,7 +358,26 @@ macro_rules! impl_tuple { Value::make_tuple(&ty, vec![$($var),*]).unwrap() } } + #[allow(non_snake_case)] + impl<$($var: ValueTyped + ToValue),*> ToValue for ($($var),*,) { + fn to_value(&self) -> Value { + let ty = <($($var),*,)>::value_type(); + let ($($var),*,) = self; + $( + let $var = $var.to_value(); + )* + Value::make_tuple(&ty, vec![$($var),*]).unwrap() + } + } + impl<$($var),*> ToRust<($($var),*,)> for Value where $( Value: ToRust<$var> ),* { + fn to_rust(&self) -> ($($var),*,) { + let mut iter = self.unwrap_tuple(); + ($( + ToRust::<$var>::to_rust(iter.next().unwrap().as_ref()), + )*) + } + } )* }; } @@ -239,6 +432,7 @@ mod tests { #[test] fn value_conversions() { + use crate::value::convert::ToValue; for (val, expect) in [ (1u8.into(), "1"), ((-123i8).into(), "-123"), @@ -252,6 +446,7 @@ mod tests { (Some(1).into(), "some(1)"), (None::.into(), "none"), (Ok::(1).into(), "ok(1)"), + (Ok::<(), ()>(()).to_value(), "ok"), (Err::("oops".into()).into(), "err(\"oops\")"), ((1,).into(), "(1)"), ((1, "str", [9; 2]).into(), "(1, \"str\", [9, 9])"), diff --git a/crates/wasm-wave/src/value/mod.rs b/crates/wasm-wave/src/value/mod.rs index 8c8e9fe413..5e95595cfc 100644 --- a/crates/wasm-wave/src/value/mod.rs +++ b/crates/wasm-wave/src/value/mod.rs @@ -1,6 +1,7 @@ //! Value enum for WAVE values. -mod convert; +#[allow(missing_docs)] +pub mod convert; #[cfg(test)] mod tests; mod ty; @@ -57,6 +58,7 @@ pub(super) enum ValueEnum { Option(OptionValue), Result(ResultValue), Flags(Flags), + Handle(Box), } #[derive(Debug, Clone, PartialEq)] @@ -156,6 +158,7 @@ impl WasmValue for Value { ValueEnum::Option(_) => WasmTypeKind::Option, ValueEnum::Result(_) => WasmTypeKind::Result, ValueEnum::Flags(_) => WasmTypeKind::Flags, + ValueEnum::Handle(_) => WasmTypeKind::Handle, } } @@ -187,6 +190,10 @@ impl WasmValue for Value { Self(ValueEnum::String(val.into())) } + fn make_handle(val: std::borrow::Cow) -> Self { + Self(ValueEnum::Handle(val.into())) + } + fn make_list( ty: &Self::Type, vals: impl IntoIterator, @@ -330,6 +337,13 @@ impl WasmValue for Value { .as_ref() .into() } + + fn unwrap_handle(&self) -> std::borrow::Cow<'_, str> { + unwrap_val!(&self.0, ValueEnum::Handle, "handle") + .as_ref() + .into() + } + fn unwrap_list(&self) -> Box> + '_> { let list = unwrap_val!(&self.0, ValueEnum::List, "list"); Box::new(list.elements.iter().map(cow)) @@ -534,6 +548,7 @@ fn check_type2(expected: &Type, val: &Value) -> Result<(), WasmValueError> { return wrong_value_type(); } } + (ValueEnum::Handle(_), Type(TypeEnum::Handle(_))) => {} (_, _) => return wrong_value_type(), }; Ok(()) diff --git a/crates/wasm-wave/src/value/tests.rs b/crates/wasm-wave/src/value/tests.rs index ba29cd8e16..a0323a97a8 100644 --- a/crates/wasm-wave/src/value/tests.rs +++ b/crates/wasm-wave/src/value/tests.rs @@ -89,6 +89,14 @@ fn option_round_trips() { test_value_round_trip(Value::make_option(&ty, None).unwrap()); } +#[test] +fn resource_round_trips() { + let ty = Type::handle("test"); + test_value_round_trip(Value::make_handle("test42".into())); + let ty = Type::handle("test"); + test_value_round_trip(Value::make_handle("test42".into())); +} + #[test] fn result_round_trips() { let no_payloads = Type::result(None, None); @@ -142,6 +150,11 @@ fn local_ty(val: &Value) -> Type { ValueEnum::Option(inner) => Type(TypeEnum::Option(inner.ty.clone())), ValueEnum::Result(inner) => Type(TypeEnum::Result(inner.ty.clone())), ValueEnum::Flags(inner) => Type(TypeEnum::Flags(inner.ty.clone())), + ValueEnum::Handle(inner) => Type(TypeEnum::Handle(std::sync::Arc::new( + crate::value::ty::HandleType { + name: inner.to_string(), + }, + ))), } } diff --git a/crates/wasm-wave/src/value/ty.rs b/crates/wasm-wave/src/value/ty.rs index 94e269c298..53710f9eb8 100644 --- a/crates/wasm-wave/src/value/ty.rs +++ b/crates/wasm-wave/src/value/ty.rs @@ -18,6 +18,7 @@ pub(super) enum TypeEnum { Option(Arc), Result(Arc), Flags(Arc), + Handle(Arc), } #[allow(missing_docs)] @@ -135,6 +136,13 @@ impl Type { Some(Self(TypeEnum::Flags(Arc::new(FlagsType { flags })))) } + /// Returns a handle type with the given name. + pub fn handle(name: &str) -> Self { + Self(TypeEnum::Handle(Arc::new(HandleType { + name: name.to_string(), + }))) + } + /// Returns a [`Type`] matching the given [`WasmType`]. Returns None if the /// given type is unsupported or otherwise invalid. pub fn from_wasm_type(ty: &impl WasmType) -> Option { @@ -194,6 +202,11 @@ pub struct FlagsType { pub(super) flags: Box<[Box]>, } +#[derive(Debug, PartialEq, Eq)] +pub struct HandleType { + pub(super) name: String, +} + impl WasmType for Type { fn kind(&self) -> WasmTypeKind { match self.0 { @@ -207,6 +220,7 @@ impl WasmType for Type { TypeEnum::Option(_) => WasmTypeKind::Option, TypeEnum::Result(_) => WasmTypeKind::Result, TypeEnum::Flags(_) => WasmTypeKind::Flags, + TypeEnum::Handle(_) => WasmTypeKind::Handle, } } @@ -269,6 +283,11 @@ impl WasmType for Type { }; Box::new(flags.flags.iter().map(|name| name.as_ref().into())) } + + fn handle_type(&self) -> &str { + let res = maybe_unwrap_type!(&self.0, TypeEnum::Handle).unwrap(); + &res.name + } } impl std::fmt::Display for Type { diff --git a/crates/wasm-wave/src/wasm/ty.rs b/crates/wasm-wave/src/wasm/ty.rs index 1f3e47610f..0904101517 100644 --- a/crates/wasm-wave/src/wasm/ty.rs +++ b/crates/wasm-wave/src/wasm/ty.rs @@ -28,6 +28,7 @@ pub enum WasmTypeKind { Option, Result, Flags, + Handle, #[doc(hidden)] Unsupported, } @@ -57,6 +58,7 @@ impl std::fmt::Display for WasmTypeKind { WasmTypeKind::Option => "option", WasmTypeKind::Result => "result", WasmTypeKind::Flags => "flags", + WasmTypeKind::Handle => "handle", WasmTypeKind::Unsupported => "<>", }) } @@ -126,6 +128,13 @@ pub trait WasmType: Clone + Sized { fn flags_names(&self) -> Box> + '_> { unimplemented!() } + + /// Returns the handle name. + /// # Panics + /// Panics if the type is not implemented (the trait default). + fn handle_type(&self) -> &str { + unimplemented!() + } } macro_rules! maybe_unwrap_type { diff --git a/crates/wasm-wave/src/wasm/val.rs b/crates/wasm-wave/src/wasm/val.rs index 127f6291ee..b87182c3ca 100644 --- a/crates/wasm-wave/src/wasm/val.rs +++ b/crates/wasm-wave/src/wasm/val.rs @@ -180,6 +180,13 @@ pub trait WasmValue: Clone + Sized { unimplemented!() } + /// Returns a new WasmValue of the given type. + /// # Panics + /// Panics if the type is not implemented (the trait default). + fn make_handle(label: Cow) -> Self { + unimplemented!() + } + /// Returns the underlying value of the WasmValue, panicing if it's the wrong type. /// # Panics /// Panics if `self` is not of the right type. @@ -316,6 +323,13 @@ pub trait WasmValue: Clone + Sized { fn unwrap_flags(&self) -> Box> + '_> { unimplemented!() } + + /// Returns the resource handle label. + /// # Panics + /// Panics if `self` is not of the right type. + fn unwrap_handle(&self) -> Cow<'_, str> { + unimplemented!() + } } macro_rules! unwrap_val { diff --git a/crates/wasm-wave/src/writer.rs b/crates/wasm-wave/src/writer.rs index 4233f359ed..8781a8c48e 100644 --- a/crates/wasm-wave/src/writer.rs +++ b/crates/wasm-wave/src/writer.rs @@ -168,6 +168,12 @@ impl Writer { self.write_str("}")?; Ok(()) } + WasmTypeKind::Handle => { + let label = val.unwrap_handle(); + self.write_str("#")?; + self.write_display(label)?; + Ok(()) + } WasmTypeKind::Unsupported => panic!("unsupported value type"), } }