diff --git a/src/protocol/parser/utils.rs b/src/protocol/parser/utils.rs new file mode 100644 index 0000000000000000000000000000000000000000..5650c9ba98491e455cc50756aa1501f339135777 --- /dev/null +++ b/src/protocol/parser/utils.rs @@ -0,0 +1,94 @@ +use crate::protocol::ast::*; +use crate::protocol::inputsource::*; +use super::symbol_table::*; +use super::type_table::*; + +/// Utility result type. +pub(crate) enum FindTypeResult<'t, 'i> { + // Found the type exactly + Found(&'t DefinedType), + // Could not match symbol + SymbolNotFound{ident_pos: InputPosition}, + // Matched part of the namespaced identifier, but not completely + SymbolPartial{ident_pos: InputPosition, symbol_pos: InputPosition, ident_iter: NamespacedIdentifierIter<'i>}, + // Symbol matched, but points to a namespace/module instead of a type + SymbolNamespace{ident_pos: InputPosition, symbol_pos: InputPosition}, +} + +impl<'t, 'i> FindTypeResult<'t, 'i> { + /// Utility function to transform the `FindTypeResult` into a `Result` where + /// `Ok` contains the resolved type, and `Err` contains a `ParseError` which + /// can be readily returned. This is the most common use. + pub(crate) fn as_parse_error(self, module_source: &InputSource) -> Result<&'t DefinedType, ParseError2> { + match self { + FindTypeResult::Found(defined_type) => Ok(defined_type), + FindTypeResult::SymbolNotFound{ident_pos} => { + Err(ParseError2::new_error( + module_source, ident_pos, + "Could not resolve this identifier to a symbol" + )) + }, + FindTypeResult::SymbolPartial{ident_pos, symbol_pos, ident_iter} => { + Err(ParseError2::new_error( + module_source, ident_pos, + "Could not fully resolve this identifier to a symbol" + ).with_postfixed_info( + module_source, symbol_pos, + &format!( + "The partial identifier '{}' was matched to this symbol", + String::from_utf8_lossy(ident_iter.returned_section()), + ) + )) + }, + FindTypeResult::SymbolNamespace{ident_pos, symbol_pos} => { + Err(ParseError2::new_error( + module_source, ident_pos, + "This identifier was resolved to a namespace instead of a type" + ).with_postfixed_info( + module_source, symbol_pos, + "This is the referenced namespace" + )) + } + } + } +} + +/// Attempt to find the type pointer to by a (root, identifier) combination. The +/// type must match exactly (no parts in the namespace iterator remaining) and +/// must be a type, not a namespace. +pub(crate) fn find_type_definition<'t, 'i>( + symbols: &SymbolTable, types: &'t TypeTable, + root_id: RootId, identifier: &'i NamespacedIdentifier +) -> FindTypeResult<'t, 'i> { + // Lookup symbol + let symbol = symbols.resolve_namespaced_symbol(root_id, identifier); + if symbol.is_none() { + return FindTypeResult::SymbolNotFound{ident_pos: identifier.position}; + } + + // Make sure we resolved it exactly + let (symbol, ident_iter) = symbol.unwrap(); + if ident_iter.num_remaining() != 0 { + return FindTypeResult::SymbolPartial{ + ident_pos: identifier.position, + symbol_pos: symbol.position, + ident_iter + }; + } + + match symbol.symbol { + Symbol::Namespace(_) => { + FindTypeResult::SymbolNamespace{ + ident_pos: identifier.position, + symbol_pos: symbol.position + } + }, + Symbol::Definition((_, definition_id)) => { + // If this function is called correctly, then we should always be + // able to match the definition's ID to an entry in the type table. + let definition = types.get_base_definition(&definition_id); + debug_assert!(definition.is_some()); + FindTypeResult::Found(definition.unwrap()) + } + } +} \ No newline at end of file