Skip to main content

rib/parser/
type_name.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Golem Source License v1.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://license.golem.cloud/LICENSE
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::parser::errors::RibParseError;
16use crate::rib_source_span::GetSourcePosition;
17use crate::wit_type::{TypeResult, WitType};
18use crate::{InferredNumber, InferredType, TypeInternal};
19use combine::parser::char;
20use combine::parser::char::{char, spaces, string};
21use combine::{attempt, between, choice, optional, sep_by, Parser};
22use combine::{parser, ParseError};
23use std::fmt::Display;
24use std::ops::Deref;
25
26// Rib grammar uses it's own `TypeName` instead of relying from any other crates to annotate types (Example: 1: u32, let x: u32 = 1;),
27// and sticks on to the  Display instance that aligns with what we see in WIT.
28// Usage of TypeName, InferredType and WitType:
29// The Rib compiler uses `InferredType` - the output of type inference. The `TypeName` used in type annotations may help with this type inference.
30// The Rib-IR which is close to running Rib code uses `WitType`, that there won't be either `TypeName` or `InferredType` in the Rib-IR.
31// Any compilation or interpreter error messages will also be using `TypeName` to show the type of the expression
32// for which we convert WitType or InferredType back to TypeName. If `InferredType` cannot be converted to `TypeName`, we explain the error displaying
33// the original expression, and there is no point displaying `InferredType` to the user.
34#[derive(Debug, Hash, Clone, Eq, PartialEq, Ord, PartialOrd)]
35pub enum TypeName {
36    Bool,
37    S8,
38    U8,
39    S16,
40    U16,
41    S32,
42    U32,
43    S64,
44    U64,
45    F32,
46    F64,
47    Chr,
48    Str,
49    List(Box<TypeName>),
50    Tuple(Vec<TypeName>),
51    Option(Box<TypeName>),
52    Result {
53        ok: Option<Box<TypeName>>,
54        error: Option<Box<TypeName>>,
55    },
56    Record(Vec<(String, Box<TypeName>)>),
57    Flags(Vec<String>),
58    Enum(Vec<String>),
59    Variant {
60        cases: Vec<(String, Option<Box<TypeName>>)>,
61    },
62}
63
64impl Display for TypeName {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        match self {
67            TypeName::Bool => write!(f, "bool"),
68            TypeName::S8 => write!(f, "s8"),
69            TypeName::U8 => write!(f, "u8"),
70            TypeName::S16 => write!(f, "s16"),
71            TypeName::U16 => write!(f, "u16"),
72            TypeName::S32 => write!(f, "s32"),
73            TypeName::U32 => write!(f, "u32"),
74            TypeName::S64 => write!(f, "s64"),
75            TypeName::U64 => write!(f, "u64"),
76            TypeName::F32 => write!(f, "f32"),
77            TypeName::F64 => write!(f, "f64"),
78            TypeName::Chr => write!(f, "char"),
79            TypeName::Str => write!(f, "string"),
80            TypeName::List(inner_type) => write!(f, "list<{inner_type}>"),
81            TypeName::Tuple(inner_types) => {
82                write!(f, "tuple<")?;
83                for (i, inner_type) in inner_types.iter().enumerate() {
84                    if i > 0 {
85                        write!(f, ", ")?;
86                    }
87                    write!(f, "{inner_type}")?;
88                }
89                write!(f, ">")
90            }
91            TypeName::Option(inner_type) => write!(f, "option<{inner_type}>"),
92            // https://component-model.bytecodealliance.org/design/wit.html#results
93            TypeName::Result { ok, error } => match (ok, error) {
94                (Some(ok), Some(error)) => {
95                    write!(f, "result<{ok}, {error}>")
96                }
97                (Some(ok), None) => {
98                    write!(f, "result<{ok}>")
99                }
100                (None, Some(error)) => {
101                    write!(f, "result<_, {error}>")
102                }
103                (None, None) => {
104                    write!(f, "result")
105                }
106            },
107            TypeName::Record(fields) => {
108                write!(f, "record {{ ")?;
109                for (i, (field, typ)) in fields.iter().enumerate() {
110                    if i > 0 {
111                        write!(f, ", ")?;
112                    }
113                    write!(f, "{field}: {typ}")?;
114                }
115                write!(f, " }}")
116            }
117            TypeName::Flags(flags) => {
118                write!(f, "flags<")?;
119                for (i, flag) in flags.iter().enumerate() {
120                    if i > 0 {
121                        write!(f, ", ")?;
122                    }
123                    write!(f, "{flag}")?;
124                }
125                write!(f, ">")
126            }
127            TypeName::Enum(cases) => {
128                write!(f, "enum {{ ")?;
129                for (i, case) in cases.iter().enumerate() {
130                    if i > 0 {
131                        write!(f, ", ")?;
132                    }
133                    write!(f, "{case}")?;
134                }
135                write!(f, " }}")
136            }
137            TypeName::Variant { cases } => {
138                write!(f, "variant {{")?;
139                for (i, (case, typ)) in cases.iter().enumerate() {
140                    if i > 0 {
141                        write!(f, ", ")?;
142                    }
143                    write!(f, "{case}")?;
144                    if let Some(typ) = typ {
145                        write!(f, "({typ})")?;
146                    }
147                }
148                write!(f, " }}")
149            }
150        }
151    }
152}
153
154impl From<&InferredNumber> for TypeName {
155    fn from(value: &InferredNumber) -> Self {
156        match value {
157            InferredNumber::S8 => TypeName::S8,
158            InferredNumber::U8 => TypeName::U8,
159            InferredNumber::S16 => TypeName::S16,
160            InferredNumber::U16 => TypeName::U16,
161            InferredNumber::S32 => TypeName::S32,
162            InferredNumber::U32 => TypeName::U32,
163            InferredNumber::S64 => TypeName::S64,
164            InferredNumber::U64 => TypeName::U64,
165            InferredNumber::F32 => TypeName::F32,
166            InferredNumber::F64 => TypeName::F64,
167        }
168    }
169}
170
171impl TryFrom<WitType> for TypeName {
172    type Error = String;
173    fn try_from(analysed_type: WitType) -> Result<Self, Self::Error> {
174        match analysed_type {
175            WitType::Bool(_) => Ok(TypeName::Bool),
176            WitType::S8(_) => Ok(TypeName::S8),
177            WitType::U8(_) => Ok(TypeName::U8),
178            WitType::S16(_) => Ok(TypeName::S16),
179            WitType::U16(_) => Ok(TypeName::U16),
180            WitType::S32(_) => Ok(TypeName::S32),
181            WitType::U32(_) => Ok(TypeName::U32),
182            WitType::S64(_) => Ok(TypeName::S64),
183            WitType::U64(_) => Ok(TypeName::U64),
184            WitType::F32(_) => Ok(TypeName::F32),
185            WitType::F64(_) => Ok(TypeName::F64),
186            WitType::Chr(_) => Ok(TypeName::Chr),
187            WitType::Str(_) => Ok(TypeName::Str),
188            WitType::List(inner_type) => Ok(TypeName::List(Box::new(
189                inner_type.inner.deref().clone().try_into()?,
190            ))),
191            WitType::Tuple(inner_type) => Ok(TypeName::Tuple(
192                inner_type
193                    .items
194                    .into_iter()
195                    .map(|x| x.try_into())
196                    .collect::<Result<_, _>>()?,
197            )),
198            WitType::Option(type_option) => Ok(TypeName::Option(Box::new(
199                type_option.inner.deref().clone().try_into()?,
200            ))),
201            WitType::Result(TypeResult { ok, err, .. }) => match (ok, err) {
202                (Some(ok), Some(err)) => Ok(TypeName::Result {
203                    ok: Some(Box::new(ok.deref().clone().try_into()?)),
204                    error: Some(Box::new(err.deref().clone().try_into()?)),
205                }),
206                (Some(ok), None) => Ok(TypeName::Result {
207                    ok: Some(Box::new(ok.deref().clone().try_into()?)),
208                    error: None,
209                }),
210                (None, Some(err)) => Ok(TypeName::Result {
211                    ok: None,
212                    error: Some(Box::new(err.deref().clone().try_into()?)),
213                }),
214                (None, None) => Ok(TypeName::Result {
215                    ok: None,
216                    error: None,
217                }),
218            },
219            WitType::Record(type_record) => {
220                let mut fields = vec![];
221                for field in type_record.fields {
222                    let name = field.name.clone();
223                    let typ = field.typ.clone();
224                    let type_name = typ.try_into()?;
225                    fields.push((name, Box::new(type_name)));
226                }
227
228                Ok(TypeName::Record(fields))
229            }
230            WitType::Flags(flags) => Ok(TypeName::Flags(flags.names)),
231            WitType::Enum(cases) => Ok(TypeName::Enum(cases.cases)),
232            WitType::Variant(cases) => {
233                let mut variant_cases = vec![];
234                for case in cases.cases {
235                    let name = case.name.clone();
236                    let typ = case.typ.clone();
237                    match typ {
238                        Some(typ) => {
239                            let type_name = typ.try_into()?;
240                            variant_cases.push((name, Some(Box::new(type_name))));
241                        }
242                        None => {
243                            variant_cases.push((name, None));
244                        }
245                    }
246                }
247                Ok(TypeName::Variant {
248                    cases: variant_cases,
249                })
250            }
251            WitType::Handle(type_handle) => {
252                Err(format!("Handle type not supported: {type_handle:?}"))
253            }
254        }
255    }
256}
257
258impl From<&TypeName> for InferredType {
259    fn from(type_name: &TypeName) -> Self {
260        match type_name {
261            TypeName::Bool => InferredType::bool(),
262            TypeName::S8 => InferredType::s8(),
263            TypeName::U8 => InferredType::u8(),
264            TypeName::S16 => InferredType::s16(),
265            TypeName::U16 => InferredType::u16(),
266            TypeName::S32 => InferredType::s32(),
267            TypeName::U32 => InferredType::u32(),
268            TypeName::S64 => InferredType::s64(),
269            TypeName::U64 => InferredType::u64(),
270            TypeName::F32 => InferredType::f32(),
271            TypeName::F64 => InferredType::f64(),
272            TypeName::Chr => InferredType::char(),
273            TypeName::Str => InferredType::string(),
274            TypeName::List(inner_type) => InferredType::list(inner_type.deref().into()),
275            TypeName::Tuple(inner_types) => {
276                InferredType::tuple(inner_types.iter().map(|t| t.into()).collect())
277            }
278            TypeName::Option(type_name) => InferredType::option(type_name.deref().into()),
279            TypeName::Result { ok, error } => InferredType::result(
280                ok.as_deref().map(|x| x.into()),
281                error.as_deref().map(|x| x.into()),
282            ),
283            TypeName::Record(fields) => InferredType::record(
284                fields
285                    .iter()
286                    .map(|(field, typ)| (field.clone(), typ.deref().into()))
287                    .collect(),
288            ),
289            TypeName::Flags(flags) => InferredType::flags(flags.clone()),
290            TypeName::Enum(cases) => InferredType::enum_(cases.clone()),
291            TypeName::Variant { cases } => InferredType::from_variant_cases(
292                cases
293                    .iter()
294                    .map(|(case_name, typ)| (case_name.clone(), typ.as_deref().map(|x| x.into())))
295                    .collect(),
296            ),
297        }
298    }
299}
300
301impl TryFrom<InferredType> for TypeName {
302    type Error = String;
303
304    fn try_from(value: InferredType) -> Result<Self, Self::Error> {
305        match value.inner.deref() {
306            TypeInternal::Bool => Ok(TypeName::Bool),
307            TypeInternal::S8 => Ok(TypeName::S8),
308            TypeInternal::U8 => Ok(TypeName::U8),
309            TypeInternal::S16 => Ok(TypeName::S16),
310            TypeInternal::U16 => Ok(TypeName::U16),
311            TypeInternal::S32 => Ok(TypeName::S32),
312            TypeInternal::U32 => Ok(TypeName::U32),
313            TypeInternal::S64 => Ok(TypeName::S64),
314            TypeInternal::U64 => Ok(TypeName::U64),
315            TypeInternal::F32 => Ok(TypeName::F32),
316            TypeInternal::F64 => Ok(TypeName::F64),
317            TypeInternal::Chr => Ok(TypeName::Chr),
318            TypeInternal::Str => Ok(TypeName::Str),
319            TypeInternal::List(inferred_type) => {
320                let verified = inferred_type.clone().try_into()?;
321                Ok(TypeName::List(Box::new(verified)))
322            }
323            TypeInternal::Tuple(inferred_types) => {
324                let mut verified_types = vec![];
325                for typ in inferred_types {
326                    let verified = typ.clone().try_into()?;
327                    verified_types.push(verified);
328                }
329                Ok(TypeName::Tuple(verified_types))
330            }
331            TypeInternal::Record(name_and_types) => {
332                let mut fields = vec![];
333                for (field, typ) in name_and_types {
334                    fields.push((field.clone(), Box::new(typ.clone().try_into()?)));
335                }
336                Ok(TypeName::Record(fields))
337            }
338            TypeInternal::Flags(flags) => Ok(TypeName::Flags(flags.clone())),
339            TypeInternal::Enum(enums) => Ok(TypeName::Enum(enums.clone())),
340            TypeInternal::Option(inferred_type) => {
341                let result = inferred_type.clone().try_into()?;
342                Ok(TypeName::Option(Box::new(result)))
343            }
344            TypeInternal::Result { ok, error } => {
345                let ok_unified = ok.as_ref().map(|ok| ok.clone().try_into()).transpose()?;
346                let err_unified = error
347                    .as_ref()
348                    .map(|err| err.clone().try_into())
349                    .transpose()?;
350                Ok(TypeName::Result {
351                    ok: ok_unified.map(Box::new),
352                    error: err_unified.map(Box::new),
353                })
354            }
355            TypeInternal::Variant(variant) => {
356                let mut cases = vec![];
357                for (case, typ) in variant {
358                    let verified = typ.clone().map(TypeName::try_from).transpose()?;
359                    cases.push((case.clone(), verified.map(Box::new)));
360                }
361                Ok(TypeName::Variant { cases })
362            }
363            TypeInternal::Resource { .. } => {
364                Err("Cannot convert a resource type to a type name".to_string())
365            }
366            TypeInternal::AllOf(_) => {
367                Err("Cannot convert a all of type to a type name".to_string())
368            }
369            TypeInternal::Unknown => {
370                Err("Cannot convert an unknown type to a type name".to_string())
371            }
372            TypeInternal::Sequence(_) => {
373                Err("Cannot convert a sequence type to a type name".to_string())
374            }
375            TypeInternal::Instance { .. } => {
376                Err("Cannot convert an instance type to a type name".to_string())
377            }
378            TypeInternal::Range { .. } => {
379                Err("Cannot convert a range type to a type name".to_string())
380            }
381        }
382    }
383}
384
385pub fn parse_basic_type<Input>() -> impl Parser<Input, Output = TypeName>
386where
387    Input: combine::Stream<Token = char>,
388    RibParseError: Into<
389        <Input::Error as ParseError<Input::Token, Input::Range, Input::Position>>::StreamError,
390    >,
391    Input::Position: GetSourcePosition,
392{
393    choice((
394        attempt(string("bool").map(|_| TypeName::Bool)),
395        attempt(string("s8").map(|_| TypeName::S8)),
396        attempt(string("u8").map(|_| TypeName::U8)),
397        attempt(string("s16").map(|_| TypeName::S16)),
398        attempt(string("u16").map(|_| TypeName::U16)),
399        attempt(string("s32").map(|_| TypeName::S32)),
400        attempt(string("u32").map(|_| TypeName::U32)),
401        attempt(string("s64").map(|_| TypeName::S64)),
402        attempt(string("u64").map(|_| TypeName::U64)),
403        attempt(string("f32").map(|_| TypeName::F32)),
404        attempt(string("f64").map(|_| TypeName::F64)),
405        attempt(string("char").map(|_| TypeName::Chr)),
406        attempt(string("string").map(|_| TypeName::Str)),
407    ))
408    .skip(spaces().silent())
409}
410
411pub fn parse_list_type<Input>() -> impl Parser<Input, Output = TypeName>
412where
413    Input: combine::Stream<Token = char>,
414    RibParseError: Into<
415        <Input::Error as ParseError<Input::Token, Input::Range, Input::Position>>::StreamError,
416    >,
417    Input::Position: GetSourcePosition,
418{
419    string("list")
420        .skip(spaces().silent())
421        .with(between(
422            char('<').skip(spaces().silent().silent()),
423            char('>').skip(spaces().silent().silent()),
424            type_name(),
425        ))
426        .map(|inner_type| TypeName::List(Box::new(inner_type)))
427}
428
429pub fn parse_option_type<Input>() -> impl Parser<Input, Output = TypeName>
430where
431    Input: combine::Stream<Token = char>,
432    RibParseError: Into<
433        <Input::Error as ParseError<Input::Token, Input::Range, Input::Position>>::StreamError,
434    >,
435    Input::Position: GetSourcePosition,
436{
437    string("option")
438        .skip(spaces().silent())
439        .with(between(
440            char('<').skip(spaces().silent().silent()),
441            char('>').skip(spaces().silent().silent()),
442            type_name(),
443        ))
444        .map(|inner_type| TypeName::Option(Box::new(inner_type)))
445}
446
447enum ResultSuccess {
448    NoType,
449    WithType(TypeName),
450}
451
452// https://component-model.bytecodealliance.org/design/wit.html#results
453pub fn parse_result_type<Input>() -> impl Parser<Input, Output = TypeName>
454where
455    Input: combine::Stream<Token = char>,
456    RibParseError: Into<
457        <Input::Error as ParseError<Input::Token, Input::Range, Input::Position>>::StreamError,
458    >,
459    Input::Position: GetSourcePosition,
460{
461    string("result")
462        .skip(spaces().silent())
463        .with(optional(between(
464            char('<').skip(spaces().silent()),
465            char('>').skip(spaces().silent()),
466            (
467                choice!(
468                    string("_")
469                        .skip(spaces().silent())
470                        .map(|_| ResultSuccess::NoType),
471                    type_name()
472                        .skip(spaces().silent())
473                        .map(ResultSuccess::WithType)
474                ),
475                optional(
476                    char(',')
477                        .skip(spaces().silent())
478                        .with(type_name().skip(spaces().silent())),
479                ),
480            ),
481        )))
482        .map(|result| match result {
483            None => TypeName::Result {
484                ok: None,
485                error: None,
486            },
487            Some((ResultSuccess::NoType, None)) => TypeName::Result {
488                ok: None,
489                error: None,
490            },
491            Some((ResultSuccess::NoType, Some(error))) => TypeName::Result {
492                ok: None,
493                error: Some(Box::new(error)),
494            },
495            Some((ResultSuccess::WithType(ok), None)) => TypeName::Result {
496                ok: Some(Box::new(ok)),
497                error: None,
498            },
499            Some((ResultSuccess::WithType(ok), Some(error))) => TypeName::Result {
500                ok: Some(Box::new(ok)),
501                error: Some(Box::new(error)),
502            },
503        })
504}
505
506pub fn parse_tuple_type<Input>() -> impl Parser<Input, Output = TypeName>
507where
508    Input: combine::Stream<Token = char>,
509    RibParseError: Into<
510        <Input::Error as ParseError<Input::Token, Input::Range, Input::Position>>::StreamError,
511    >,
512    Input::Position: GetSourcePosition,
513{
514    string("tuple")
515        .skip(spaces().silent())
516        .with(between(
517            char('<').skip(spaces().silent()),
518            char('>').skip(spaces().silent()),
519            sep_by(type_name(), char(',').skip(spaces().silent())),
520        ))
521        .map(TypeName::Tuple)
522}
523
524pub fn type_name_<Input>() -> impl Parser<Input, Output = TypeName>
525where
526    Input: combine::Stream<Token = char>,
527    RibParseError: Into<
528        <Input::Error as ParseError<Input::Token, Input::Range, Input::Position>>::StreamError,
529    >,
530    Input::Position: GetSourcePosition,
531{
532    spaces().silent().with(choice((
533        attempt(parse_basic_type()),
534        attempt(parse_list_type()),
535        attempt(parse_tuple_type()),
536        attempt(parse_option_type()),
537        attempt(parse_result_type()),
538    )))
539}
540
541parser! {
542    pub fn type_name[Input]()(Input) -> TypeName
543     where [Input: combine::Stream<Token = char>, RibParseError: Into<<Input::Error as ParseError<Input::Token, Input::Range, Input::Position>>::StreamError>, Input::Position: GetSourcePosition]
544    {
545       type_name_()
546    }
547}
548
549#[cfg(test)]
550mod type_name_tests {
551    use combine::stream::position;
552    use combine::EasyParser;
553    use test_r::test;
554
555    use super::*;
556
557    fn parse_and_compare(input: &str, expected: TypeName) {
558        let written = format!("{expected}");
559        let result1 = type_name()
560            .easy_parse(position::Stream::new(input))
561            .map(|x| x.0);
562        let result2 = type_name()
563            .easy_parse(position::Stream::new(written.as_str()))
564            .map(|x| x.0);
565        assert_eq!(result1, Ok(expected.clone()));
566        assert_eq!(result2, Ok(expected));
567    }
568
569    #[test]
570    fn test_basic_types() {
571        parse_and_compare("bool", TypeName::Bool);
572        parse_and_compare("s8", TypeName::S8);
573        parse_and_compare("u8", TypeName::U8);
574        parse_and_compare("s16", TypeName::S16);
575        parse_and_compare("u16", TypeName::U16);
576        parse_and_compare("s32", TypeName::S32);
577        parse_and_compare("u32", TypeName::U32);
578        parse_and_compare("s64", TypeName::S64);
579        parse_and_compare("u64", TypeName::U64);
580        parse_and_compare("f32", TypeName::F32);
581        parse_and_compare("f64", TypeName::F64);
582        parse_and_compare("char", TypeName::Chr);
583        parse_and_compare("string", TypeName::Str);
584    }
585
586    #[test]
587    fn test_list_type_name() {
588        parse_and_compare("list<u8>", TypeName::List(Box::new(TypeName::U8)));
589        parse_and_compare(
590            "list<list<f32>>",
591            TypeName::List(Box::new(TypeName::List(Box::new(TypeName::F32)))),
592        );
593    }
594
595    #[test]
596    fn test_tuple_type_name() {
597        parse_and_compare(
598            "tuple<u8, u16>",
599            TypeName::Tuple(vec![TypeName::U8, TypeName::U16]),
600        );
601        parse_and_compare(
602            "tuple<s32, list<u8>>",
603            TypeName::Tuple(vec![TypeName::S32, TypeName::List(Box::new(TypeName::U8))]),
604        );
605        parse_and_compare(
606            "tuple<tuple<s8, s16>, u32>",
607            TypeName::Tuple(vec![
608                TypeName::Tuple(vec![TypeName::S8, TypeName::S16]),
609                TypeName::U32,
610            ]),
611        );
612    }
613
614    #[test]
615    fn test_option_type_name() {
616        parse_and_compare("option<u8>", TypeName::Option(Box::new(TypeName::U8)));
617        parse_and_compare(
618            "option<list<f32>>",
619            TypeName::Option(Box::new(TypeName::List(Box::new(TypeName::F32)))),
620        );
621    }
622
623    #[test]
624    fn test_nested_types() {
625        parse_and_compare(
626            "list<tuple<u8, s8>>",
627            TypeName::List(Box::new(TypeName::Tuple(vec![TypeName::U8, TypeName::S8]))),
628        );
629        parse_and_compare(
630            "tuple<list<u16>, list<f64>>",
631            TypeName::Tuple(vec![
632                TypeName::List(Box::new(TypeName::U16)),
633                TypeName::List(Box::new(TypeName::F64)),
634            ]),
635        );
636    }
637
638    #[test]
639    fn test_spaces_around_types() {
640        parse_and_compare("  u8  ", TypeName::U8);
641        parse_and_compare("list< u8 >", TypeName::List(Box::new(TypeName::U8)));
642        parse_and_compare(
643            "tuple< s32 , list< u8 > >",
644            TypeName::Tuple(vec![TypeName::S32, TypeName::List(Box::new(TypeName::U8))]),
645        );
646    }
647}