Changeset - 19b752d30f04
[Not reviewed]
0 5 0
MH - 4 years ago 2021-04-02 14:56:59
contact@maxhenger.nl
cleanup old NamespacedIdentifier
5 files changed with 71 insertions and 209 deletions:
0 comments (0 inline, 0 general)
src/protocol/ast.rs
Show inline comments
 
@@ -536,815 +536,695 @@ pub struct PragmaModule {
 
    pub position: InputPosition,
 
    pub value: Vec<u8>,
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct PragmaOld {
 
    pub this: PragmaId,
 
    // Phase 1: parser
 
    pub position: InputPosition,
 
    pub value: Vec<u8>,
 
}
 

	
 
impl SyntaxElement for PragmaOld {
 
    fn position(&self) -> InputPosition {
 
        self.position
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub enum Import {
 
    Module(ImportModule),
 
    Symbols(ImportSymbols)
 
}
 

	
 
impl Import {
 
    pub(crate) fn as_module(&self) -> &ImportModule {
 
        match self {
 
            Import::Module(m) => m,
 
            _ => panic!("Unable to cast 'Import' to 'ImportModule'")
 
        }
 
    }
 
    pub(crate) fn as_symbols(&self) -> &ImportSymbols {
 
        match self {
 
            Import::Symbols(m) => m,
 
            _ => panic!("Unable to cast 'Import' to 'ImportSymbols'")
 
        }
 
    }
 
}
 

	
 
impl SyntaxElement for Import {
 
    fn position(&self) -> InputPosition {
 
        match self {
 
            Import::Module(m) => m.position,
 
            Import::Symbols(m) => m.position
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct ImportModule {
 
    pub this: ImportId,
 
    // Phase 1: parser
 
    pub position: InputPosition,
 
    pub module_name: Vec<u8>,
 
    pub alias: Identifier,
 
    // Phase 2: module resolving
 
    pub module_id: Option<RootId>,
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct AliasedSymbol {
 
    // Phase 1: parser
 
    pub position: InputPosition,
 
    pub name: Identifier,
 
    pub alias: Identifier,
 
    // Phase 2: symbol resolving
 
    pub definition_id: Option<DefinitionId>,
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct ImportSymbols {
 
    pub this: ImportId,
 
    // Phase 1: parser
 
    pub position: InputPosition,
 
    pub module_name: Vec<u8>,
 
    // Phase 2: module resolving
 
    pub module_id: Option<RootId>,
 
    // Phase 1&2
 
    // if symbols is empty, then we implicitly import all symbols without any
 
    // aliases for them. If it is not empty, then symbols are explicitly
 
    // specified, and optionally given an alias.
 
    pub symbols: Vec<AliasedSymbol>,
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct Identifier {
 
    pub position: InputPosition,
 
    pub value: Vec<u8>
 
}
 

	
 
impl PartialEq for Identifier {
 
    fn eq(&self, other: &Self) -> bool {
 
        return self.value == other.value
 
    }
 
}
 

	
 
impl PartialEq<NamespacedIdentifier> for Identifier {
 
    fn eq(&self, other: &NamespacedIdentifier) -> bool {
 
        return self.value == other.value
 
impl Display for Identifier {
 
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
 
        // A source identifier is in ASCII range.
 
        write!(f, "{}", String::from_utf8_lossy(&self.value))
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub enum NamespacedIdentifierPart {
 
    // Regular identifier
 
    Identifier{start: u16, end: u16},
 
    // Polyargs associated with a preceding identifier
 
    PolyArgs{start: u16, end: u16},
 
}
 

	
 
impl NamespacedIdentifierPart {
 
    pub(crate) fn is_identifier(&self) -> bool {
 
        match self {
 
            NamespacedIdentifierPart::Identifier{..} => true,
 
            NamespacedIdentifierPart::PolyArgs{..} => false,
 
        }
 
    }
 

	
 
    pub(crate) fn as_identifier(&self) -> (u16, u16) {
 
        match self {
 
            NamespacedIdentifierPart::Identifier{start, end} => (*start, *end),
 
            NamespacedIdentifierPart::PolyArgs{..} => {
 
                unreachable!("Tried to obtain {:?} as Identifier", self);
 
            }
 
        }
 
    }
 

	
 
    pub(crate) fn as_poly_args(&self) -> (u16, u16) {
 
        match self {
 
            NamespacedIdentifierPart::PolyArgs{start, end} => (*start, *end),
 
            NamespacedIdentifierPart::Identifier{..} => {
 
                unreachable!("Tried to obtain {:?} as PolyArgs", self)
 
            }
 
        }
 
    }
 
}
 

	
 
/// An identifier with optional namespaces and polymorphic variables. Note that 
 
/// we allow each identifier to be followed by polymorphic arguments during the 
 
/// parsing phase (e.g. Foo<A,B>::Bar<C,D>::Qux). But in our current language 
 
/// implementation we can only have valid namespaced identifier that contain one
 
/// set of polymorphic arguments at the appropriate position.
 
/// TODO: @tokens Reimplement/rename once we have a tokenizer
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct NamespacedIdentifier2 {
 
pub struct NamespacedIdentifier {
 
    pub position: InputPosition,
 
    pub value: Vec<u8>, // Full name as it resides in the input source
 
    pub poly_args: Vec<ParserTypeId>, // All poly args littered throughout the namespaced identifier
 
    pub parts: Vec<NamespacedIdentifierPart>, // Indices into value/poly_args
 
}
 

	
 
impl NamespacedIdentifier2 {
 
impl NamespacedIdentifier {
 
    /// Returns the identifier value without any of the specific polymorphic
 
    /// arguments.
 
    pub fn strip_poly_args(&self) -> Vec<u8> {
 
        debug_assert!(!self.parts.is_empty() && self.parts[0].is_identifier());
 

	
 
        let mut result = Vec::with_capacity(self.value.len());
 
        let mut iter = self.iter();
 
        let (first_ident, _) = iter.next().unwrap();
 
        result.extend(first_ident);
 

	
 
        for (ident, _) in iter.next() {
 
            result.push(b':');
 
            result.push(b':');
 
            result.extend(ident);
 
        }
 

	
 
        result
 
    }
 

	
 
    /// Returns an iterator of the elements in the namespaced identifier
 
    pub fn iter(&self) -> NamespacedIdentifier2Iter {
 
        return NamespacedIdentifier2Iter{
 
    pub fn iter(&self) -> NamespacedIdentifierIter {
 
        return NamespacedIdentifierIter{
 
            identifier: self,
 
            element_idx: 0
 
        }
 
    }
 

	
 
    pub fn get_poly_args(&self) -> Option<&[ParserTypeId]> {
 
        let has_poly_args = self.parts.iter().any(|v| !v.is_identifier());
 
        if has_poly_args {
 
            Some(&self.poly_args)
 
        } else {
 
            None
 
        }
 
    }
 

	
 
    // Check if two namespaced identifiers match eachother when not considering
 
    // the polymorphic arguments
 
    pub fn matches_namespaced_identifier(&self, other: &Self) -> bool {
 
        let mut iter_self = self.iter();
 
        let mut iter_other = other.iter();
 

	
 
        loop {
 
            let val_self = iter_self.next();
 
            let val_other = iter_other.next();
 
            if val_self.is_some() != val_other.is_some() {
 
                // One is longer than the other
 
                return false;
 
            }
 
            if val_self.is_none() {
 
                // Both are none
 
                return true;
 
            }
 

	
 
            // Both are something
 
            let (val_self, _) = val_self.unwrap();
 
            let (val_other, _) = val_other.unwrap();
 
            if val_self != val_other { return false; }
 
        }
 
    }
 

	
 
    // Check if the namespaced identifier matches an identifier when not 
 
    // considering the polymorphic arguments
 
    pub fn matches_identifier(&self, other: &Identifier) -> bool {
 
        let mut iter = self.iter();
 
        let (first_ident, _) = iter.next().unwrap();
 
        if first_ident != other.value { 
 
            return false;
 
        }
 

	
 
        if iter.next().is_some() {
 
            return false;
 
        }
 

	
 
        return true;
 
    }
 
}
 

	
 
/// Iterator over elements of the namespaced identifier. The element index will
 
/// only ever be at the start of an identifier element.
 
#[derive(Debug)]
 
pub struct NamespacedIdentifier2Iter<'a> {
 
    identifier: &'a NamespacedIdentifier2,
 
pub struct NamespacedIdentifierIter<'a> {
 
    identifier: &'a NamespacedIdentifier,
 
    element_idx: usize,
 
}
 

	
 
impl<'a> Iterator for NamespacedIdentifier2Iter<'a> {
 
impl<'a> Iterator for NamespacedIdentifierIter<'a> {
 
    type Item = (&'a [u8], Option<&'a [ParserTypeId]>);
 
    fn next(&mut self) -> Option<Self::Item> {
 
        match self.get(self.element_idx) {
 
            Some((ident, poly)) => {
 
                self.element_idx += 1;
 
                if poly.is_some() {
 
                    self.element_idx += 1;
 
                }
 
                Some((ident, poly))
 
            },
 
            None => None
 
        }
 
    }
 
}
 

	
 
impl<'a> NamespacedIdentifier2Iter<'a> {
 
impl<'a> NamespacedIdentifierIter<'a> {
 
    /// Returns number of parts iterated over, may not correspond to number of
 
    /// times one called `next()` because returning an identifier with 
 
    /// polymorphic arguments increments the internal counter by 2.
 
    pub fn num_returned(&self) -> usize {
 
        return self.element_idx;
 
    }
 

	
 
    pub fn num_remaining(&self) -> usize {
 
        return self.identifier.parts.len() - self.element_idx;
 
    }
 

	
 
    pub fn returned_section(&self) -> &[u8] {
 
        if self.element_idx == 0 { return &self.identifier.value[0..0]; }
 

	
 
        let last_idx = match &self.identifier.parts[self.element_idx - 1] {
 
            NamespacedIdentifierPart::Identifier{end, ..} => *end,
 
            NamespacedIdentifierPart::PolyArgs{end, ..} => *end,
 
        };
 

	
 
        return &self.identifier.value[..last_idx as usize];
 
    }
 

	
 
    /// Returns a specific element from the namespaced identifier
 
    pub fn get(&self, idx: usize) -> Option<<Self as Iterator>::Item> {
 
        if idx >= self.identifier.parts.len() { 
 
            return None 
 
        }
 

	
 
        let cur_part = &self.identifier.parts[idx];
 
        let next_part = self.identifier.parts.get(idx + 1);
 

	
 
        let (ident_start, ident_end) = cur_part.as_identifier();
 
        let poly_slice = match next_part {
 
            Some(part) => match part {
 
                NamespacedIdentifierPart::Identifier{..} => None,
 
                NamespacedIdentifierPart::PolyArgs{start, end} => Some(
 
                    &self.identifier.poly_args[*start as usize..*end as usize]
 
                ),
 
            },
 
            None => None
 
        };
 

	
 
        Some((
 
            &self.identifier.value[ident_start as usize..ident_end as usize],
 
            poly_slice
 
        ))
 
    }
 

	
 
    /// Returns the previously returend index into the parts array of the 
 
    /// identifier.
 
    pub fn prev_idx(&self) -> Option<usize> {
 
        if self.element_idx == 0 { 
 
            return None;
 
        };
 
        
 
        if self.identifier.parts[self.element_idx - 1].is_identifier() { 
 
            return Some(self.element_idx - 1);
 
        }
 

	
 
        // Previous part had polymorphic arguments, so the one before that must
 
        // be an identifier (if well formed)
 
        debug_assert!(self.element_idx >= 2 && self.identifier.parts[self.element_idx - 2].is_identifier());
 
        return Some(self.element_idx - 2)
 
    }
 

	
 
    /// Returns the previously returned result from `next()`
 
    pub fn prev(&self) -> Option<<Self as Iterator>::Item> {
 
        match self.prev_idx() {
 
            None => None,
 
            Some(idx) => self.get(idx)
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct NamespacedIdentifier {
 
    pub position: InputPosition,
 
    pub num_namespaces: u8,
 
    pub value: Vec<u8>,
 
}
 

	
 
impl NamespacedIdentifier {
 
    pub(crate) fn iter(&self) -> NamespacedIdentifierIter {
 
        NamespacedIdentifierIter{
 
            value: &self.value,
 
            cur_offset: 0,
 
            num_returned: 0,
 
            num_total: self.num_namespaces
 
        }
 
    }
 
}
 

	
 
impl PartialEq for NamespacedIdentifier {
 
    fn eq(&self, other: &Self) -> bool {
 
        return self.value == other.value
 
    }
 
}
 

	
 
impl PartialEq<Identifier> for NamespacedIdentifier {
 
    fn eq(&self, other: &Identifier) -> bool {
 
        return self.value == other.value;
 
    }
 
}
 

	
 
// TODO: Just keep ref to NamespacedIdentifier
 
pub(crate) struct NamespacedIdentifierIter<'a> {
 
    value: &'a Vec<u8>,
 
    cur_offset: usize,
 
    num_returned: u8,
 
    num_total: u8,
 
}
 

	
 
impl<'a> NamespacedIdentifierIter<'a> {
 
    pub(crate) fn num_returned(&self) -> u8 {
 
        return self.num_returned;
 
    }
 
    pub(crate) fn num_remaining(&self) -> u8 {
 
        return self.num_total - self.num_returned
 
    }
 
    pub(crate) fn returned_section(&self) -> &[u8] {
 
        // Offset always includes the two trailing ':' characters
 
        let end = if self.cur_offset >= 2 { self.cur_offset - 2 } else { self.cur_offset };
 
        return &self.value[..end]
 
    }
 
}
 

	
 
impl<'a> Iterator for NamespacedIdentifierIter<'a> {
 
    type Item = &'a [u8];
 
    fn next(&mut self) -> Option<Self::Item> {
 
        if self.cur_offset >= self.value.len() {
 
            debug_assert_eq!(self.num_returned, self.num_total);
 
            None
 
        } else {
 
            debug_assert!(self.num_returned < self.num_total);
 
            let start = self.cur_offset;
 
            let mut end = start;
 
            while end < self.value.len() - 1 {
 
                if self.value[end] == b':' && self.value[end + 1] == b':' {
 
                    self.cur_offset = end + 2;
 
                    self.num_returned += 1;
 
                    return Some(&self.value[start..end]);
 
                }
 
                end += 1;
 
            }
 

	
 
            // If NamespacedIdentifier is constructed properly, then we cannot
 
            // end with "::" in the value, so
 
            debug_assert!(end == 0 || (self.value[end - 1] != b':' && self.value[end] != b':'));
 
            debug_assert_eq!(self.num_returned + 1, self.num_total);
 
            self.cur_offset = self.value.len();
 
            self.num_returned += 1;
 
            return Some(&self.value[start..]);
 
        }
 
    }
 
}
 

	
 
impl Display for Identifier {
 
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
 
        // A source identifier is in ASCII range.
 
        write!(f, "{}", String::from_utf8_lossy(&self.value))
 
    }
 
}
 

	
 
/// TODO: @types Remove the Message -> Byte hack at some point...
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub enum ParserTypeVariant {
 
    // Basic builtin
 
    Message,
 
    Bool,
 
    Byte,
 
    Short,
 
    Int,
 
    Long,
 
    String,
 
    // Literals (need to get concrete builtin type during typechecking)
 
    IntegerLiteral,
 
    Inferred,
 
    // Complex builtins
 
    Array(ParserTypeId), // array of a type
 
    Input(ParserTypeId), // typed input endpoint of a channel
 
    Output(ParserTypeId), // typed output endpoint of a channel
 
    Symbolic(SymbolicParserType), // symbolic type (definition or polyarg)
 
}
 

	
 
impl ParserTypeVariant {
 
    pub(crate) fn supports_polymorphic_args(&self) -> bool {
 
        use ParserTypeVariant::*;
 
        match self {
 
            Message | Bool | Byte | Short | Int | Long | String | IntegerLiteral | Inferred => false,
 
            _ => true
 
        }
 
    }
 
}
 

	
 
/// ParserType is a specification of a type during the parsing phase and initial
 
/// linker/validator phase of the compilation process. These types may be
 
/// (partially) inferred or represent literals (e.g. a integer whose bytesize is
 
/// not yet determined).
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct ParserType {
 
    pub this: ParserTypeId,
 
    pub pos: InputPosition,
 
    pub variant: ParserTypeVariant,
 
}
 

	
 
/// SymbolicParserType is the specification of a symbolic type. During the
 
/// parsing phase we will only store the identifier of the type. During the
 
/// validation phase we will determine whether it refers to a user-defined type,
 
/// or a polymorphic argument. After the validation phase it may still be the
 
/// case that the resulting `variant` will not pass the typechecker.
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct SymbolicParserType {
 
    // Phase 1: parser
 
    pub identifier: NamespacedIdentifier2,
 
    pub identifier: NamespacedIdentifier,
 
    // Phase 2: validation/linking (for types in function/component bodies) and
 
    //  type table construction (for embedded types of structs/unions)
 
    pub poly_args2: Vec<ParserTypeId>, // taken from identifier or inferred
 
    pub variant: Option<SymbolicParserTypeVariant>
 
}
 

	
 
/// Specifies whether the symbolic type points to an actual user-defined type,
 
/// or whether it points to a polymorphic argument within the definition (e.g.
 
/// a defined variable `T var` within a function `int func<T>()`
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub enum SymbolicParserTypeVariant {
 
    Definition(DefinitionId),
 
    // TODO: figure out if I need the DefinitionId here
 
    PolyArg(DefinitionId, usize), // index of polyarg in the definition
 
}
 

	
 
/// ConcreteType is the representation of a type after resolving symbolic types
 
/// and performing type inference
 
#[derive(Debug, Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
 
pub enum ConcreteTypePart {
 
    // Markers for the use of polymorphic types within a procedure's body that
 
    // refer to polymorphic variables on the procedure's definition. Different
 
    // from markers in the `InferenceType`, these will not contain nested types.
 
    Marker(usize),
 
    // Special types (cannot be explicitly constructed by the programmer)
 
    Void,
 
    // Builtin types without nested types
 
    Message,
 
    Bool,
 
    Byte,
 
    Short,
 
    Int,
 
    Long,
 
    String,
 
    // Builtin types with one nested type
 
    Array,
 
    Slice,
 
    Input,
 
    Output,
 
    // User defined type with any number of nested types
 
    Instance(DefinitionId, usize),
 
}
 

	
 
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
 
pub struct ConcreteType {
 
    pub(crate) parts: Vec<ConcreteTypePart>
 
}
 

	
 
impl Default for ConcreteType {
 
    fn default() -> Self {
 
        Self{ parts: Vec::new() }
 
    }
 
}
 

	
 
impl ConcreteType {
 
    pub(crate) fn has_marker(&self) -> bool {
 
        self.parts
 
            .iter()
 
            .any(|p| {
 
                if let ConcreteTypePart::Marker(_) = p { true } else { false }
 
            })
 
    }
 
}
 

	
 
// TODO: Remove at some point
 
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
 
pub enum PrimitiveType {
 
    Unassigned,
 
    Input,
 
    Output,
 
    Message,
 
    Boolean,
 
    Byte,
 
    Short,
 
    Int,
 
    Long,
 
    Symbolic(PrimitiveSymbolic)
 
}
 

	
 
// TODO: @cleanup, remove PartialEq implementations
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct PrimitiveSymbolic {
 
    // Phase 1: parser
 
    pub(crate) identifier: NamespacedIdentifier, // TODO: @remove at some point, also remove NSIdent itself
 
    // Phase 2: typing
 
    pub(crate) definition: Option<DefinitionId>
 
}
 

	
 
impl PartialEq for PrimitiveSymbolic {
 
    fn eq(&self, other: &Self) -> bool {
 
        self.identifier == other.identifier
 
    }
 
}
 
impl Eq for PrimitiveSymbolic{}
 

	
 
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
 
pub struct Type {
 
    pub primitive: PrimitiveType,
 
    pub array: bool,
 
}
 

	
 
#[allow(dead_code)]
 
impl Type {
 
    pub const UNASSIGNED: Type = Type { primitive: PrimitiveType::Unassigned, array: false };
 

	
 
    pub const INPUT: Type = Type { primitive: PrimitiveType::Input, array: false };
 
    pub const OUTPUT: Type = Type { primitive: PrimitiveType::Output, array: false };
 
    pub const MESSAGE: Type = Type { primitive: PrimitiveType::Message, array: false };
 
    pub const BOOLEAN: Type = Type { primitive: PrimitiveType::Boolean, array: false };
 
    pub const BYTE: Type = Type { primitive: PrimitiveType::Byte, array: false };
 
    pub const SHORT: Type = Type { primitive: PrimitiveType::Short, array: false };
 
    pub const INT: Type = Type { primitive: PrimitiveType::Int, array: false };
 
    pub const LONG: Type = Type { primitive: PrimitiveType::Long, array: false };
 

	
 
    pub const INPUT_ARRAY: Type = Type { primitive: PrimitiveType::Input, array: true };
 
    pub const OUTPUT_ARRAY: Type = Type { primitive: PrimitiveType::Output, array: true };
 
    pub const MESSAGE_ARRAY: Type = Type { primitive: PrimitiveType::Message, array: true };
 
    pub const BOOLEAN_ARRAY: Type = Type { primitive: PrimitiveType::Boolean, array: true };
 
    pub const BYTE_ARRAY: Type = Type { primitive: PrimitiveType::Byte, array: true };
 
    pub const SHORT_ARRAY: Type = Type { primitive: PrimitiveType::Short, array: true };
 
    pub const INT_ARRAY: Type = Type { primitive: PrimitiveType::Int, array: true };
 
    pub const LONG_ARRAY: Type = Type { primitive: PrimitiveType::Long, array: true };
 
}
 

	
 
impl Display for Type {
 
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
 
        match &self.primitive {
 
            PrimitiveType::Unassigned => {
 
                write!(f, "unassigned")?;
 
            }
 
            PrimitiveType::Input => {
 
                write!(f, "in")?;
 
            }
 
            PrimitiveType::Output => {
 
                write!(f, "out")?;
 
            }
 
            PrimitiveType::Message => {
 
                write!(f, "msg")?;
 
            }
 
            PrimitiveType::Boolean => {
 
                write!(f, "boolean")?;
 
            }
 
            PrimitiveType::Byte => {
 
                write!(f, "byte")?;
 
            }
 
            PrimitiveType::Short => {
 
                write!(f, "short")?;
 
            }
 
            PrimitiveType::Int => {
 
                write!(f, "int")?;
 
            }
 
            PrimitiveType::Long => {
 
                write!(f, "long")?;
 
            }
 
            PrimitiveType::Symbolic(data) => {
 
                // Type data is in ASCII range.
 
                if let Some(id) = &data.definition {
 
                    write!(
 
                        f, "Symbolic({}, id: {})", 
 
                        String::from_utf8_lossy(&data.identifier.value),
 
                        id.index
 
                    )?;
 
                } else {
 
                    write!(
 
                        f, "Symbolic({}, id: Unresolved)",
 
                        String::from_utf8_lossy(&data.identifier.value)
 
                    )?;
 
                }
 
            }
 
        }
 
        if self.array {
 
            write!(f, "[]")
 
        } else {
 
            Ok(())
 
        }
 
    }
 
}
 

	
 
type LiteralCharacter = Vec<u8>;
 
type LiteralInteger = i64; // TODO: @int_literal
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub enum Literal {
 
    Null, // message
 
    True,
 
    False,
 
    Character(LiteralCharacter),
 
    Integer(LiteralInteger),
 
    Struct(LiteralStruct),
 
}
 

	
 
impl Literal {
 
    pub(crate) fn as_struct(&self) -> &LiteralStruct {
 
        if let Literal::Struct(literal) = self{
 
            literal
 
        } else {
 
            unreachable!("Attempted to obtain {:?} as Literal::Struct", self)
 
        }
 
    }
 

	
 
    pub(crate) fn as_struct_mut(&mut self) -> &mut LiteralStruct {
 
        if let Literal::Struct(literal) = self{
 
            literal
 
        } else {
 
            unreachable!("Attempted to obtain {:?} as Literal::Struct", self)
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct LiteralStructField {
 
    // Phase 1: parser
 
    pub(crate) identifier: Identifier,
 
    pub(crate) value: ExpressionId,
 
    // Phase 2: linker
 
    pub(crate) field_idx: usize, // in struct definition
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct LiteralStruct {
 
    // Phase 1: parser
 
    pub(crate) identifier: NamespacedIdentifier2,
 
    pub(crate) identifier: NamespacedIdentifier,
 
    pub(crate) fields: Vec<LiteralStructField>,
 
    // Phase 2: linker
 
    pub(crate) poly_args2: Vec<ParserTypeId>, // taken from identifier
 
    pub(crate) definition: Option<DefinitionId>
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct LiteralEnum {
 
    // Phase 1: parser
 
    pub(crate) identifier: NamespacedIdentifier2,
 
    pub(crate) poly_args: Vec<ParserTypeId>,
 
    pub(crate) identifier: NamespacedIdentifier,
 
    // Phase 2: linker
 
    pub(crate) poly_args2: Vec<ParserTypeId>,
 
    pub(crate) definition: Option<DefinitionId>,
 
    pub(crate) variant_idx: usize,
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub enum Method {
 
    Get,
 
    Put,
 
    Fires,
 
    Create,
 
    Symbolic(MethodSymbolic)
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct MethodSymbolic {
 
    pub(crate) identifier: NamespacedIdentifier2,
 
    pub(crate) identifier: NamespacedIdentifier,
 
    pub(crate) definition: Option<DefinitionId>
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub enum Field {
 
    Length,
 
    Symbolic(FieldSymbolic),
 
}
 
impl Field {
 
    pub fn is_length(&self) -> bool {
 
        match self {
 
            Field::Length => true,
 
            _ => false,
 
        }
 
    }
 

	
 
    pub fn as_symbolic(&self) -> &FieldSymbolic {
 
        match self {
 
            Field::Symbolic(v) => v,
 
            _ => unreachable!("attempted to get Field::Symbolic from {:?}", self)
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct FieldSymbolic {
 
    // Phase 1: Parser
 
    pub(crate) identifier: Identifier,
 
    // Phase 3: Typing
 
    pub(crate) definition: Option<DefinitionId>,
 
    pub(crate) field_idx: usize,
 
}
 

	
 
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
 
pub enum Scope {
 
    Definition(DefinitionId),
 
    Regular(BlockStatementId),
 
    Synchronous((SynchronousStatementId, BlockStatementId)),
 
}
 

	
 
impl Scope {
 
    pub fn is_block(&self) -> bool {
 
        match &self {
 
            Scope::Definition(_) => false,
 
            Scope::Regular(_) => true,
 
            Scope::Synchronous(_) => true,
 
        }
 
    }
 
    pub fn to_block(&self) -> BlockStatementId {
 
        match &self {
 
            Scope::Regular(id) => *id,
 
            Scope::Synchronous((_, id)) => *id,
 
            _ => panic!("unable to get BlockStatement from Scope")
 
        }
 
    }
 
}
 

	
 
pub trait VariableScope {
 
    fn parent_scope(&self, h: &Heap) -> Option<Scope>;
 
    fn get_variable(&self, h: &Heap, id: &Identifier) -> Option<VariableId>;
 
}
 

	
 
impl VariableScope for Scope {
 
    fn parent_scope(&self, h: &Heap) -> Option<Scope> {
 
        match self {
 
            Scope::Definition(def) => h[*def].parent_scope(h),
 
            Scope::Regular(stmt) => h[*stmt].parent_scope(h),
 
            Scope::Synchronous((stmt, _)) => h[*stmt].parent_scope(h),
 
        }
 
    }
 
    fn get_variable(&self, h: &Heap, id: &Identifier) -> Option<VariableId> {
 
        match self {
 
            Scope::Definition(def) => h[*def].get_variable(h, id),
 
            Scope::Regular(stmt) => h[*stmt].get_variable(h, id),
 
            Scope::Synchronous((stmt, _)) => h[*stmt].get_variable(h, id),
 
        }
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub enum Variable {
 
    Parameter(Parameter),
 
    Local(Local),
 
}
 

	
 
impl Variable {
 
    pub fn identifier(&self) -> &Identifier {
 
        match self {
 
            Variable::Parameter(var) => &var.identifier,
 
            Variable::Local(var) => &var.identifier,
 
        }
 
    }
 
    pub fn is_parameter(&self) -> bool {
 
        match self {
 
            Variable::Parameter(_) => true,
 
            _ => false,
 
@@ -2594,109 +2474,109 @@ pub struct SlicingExpression {
 
    pub position: InputPosition,
 
    pub subject: ExpressionId,
 
    pub from_index: ExpressionId,
 
    pub to_index: ExpressionId,
 
    // Phase 2: linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
impl SyntaxElement for SlicingExpression {
 
    fn position(&self) -> InputPosition {
 
        self.position
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct SelectExpression {
 
    pub this: SelectExpressionId,
 
    // Phase 1: parser
 
    pub position: InputPosition,
 
    pub subject: ExpressionId,
 
    pub field: Field,
 
    // Phase 2: linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
impl SyntaxElement for SelectExpression {
 
    fn position(&self) -> InputPosition {
 
        self.position
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct ArrayExpression {
 
    pub this: ArrayExpressionId,
 
    // Phase 1: parser
 
    pub position: InputPosition,
 
    pub elements: Vec<ExpressionId>,
 
    // Phase 2: linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
impl SyntaxElement for ArrayExpression {
 
    fn position(&self) -> InputPosition {
 
        self.position
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct CallExpression {
 
    pub this: CallExpressionId,
 
    // Phase 1: parser
 
    pub position: InputPosition,
 
    pub method: Method,
 
    pub arguments: Vec<ExpressionId>,
 
    pub poly_args: Vec<ParserTypeId>,
 
    // Phase 2: linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
impl SyntaxElement for CallExpression {
 
    fn position(&self) -> InputPosition {
 
        self.position
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct LiteralExpression {
 
    pub this: LiteralExpressionId,
 
    // Phase 1: parser
 
    pub position: InputPosition,
 
    pub value: Literal,
 
    // Phase 2: linker
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
impl SyntaxElement for LiteralExpression {
 
    fn position(&self) -> InputPosition {
 
        self.position
 
    }
 
}
 

	
 
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
 
pub struct VariableExpression {
 
    pub this: VariableExpressionId,
 
    // Phase 1: parser
 
    pub position: InputPosition,
 
    pub identifier: NamespacedIdentifier2,
 
    pub identifier: NamespacedIdentifier,
 
    // Phase 2: linker
 
    pub declaration: Option<VariableId>,
 
    pub parent: ExpressionParent,
 
    // Phase 3: type checking
 
    pub concrete_type: ConcreteType,
 
}
 

	
 
impl SyntaxElement for VariableExpression {
 
    fn position(&self) -> InputPosition {
 
        self.position
 
    }
 
}
src/protocol/lexer.rs
Show inline comments
 
use crate::protocol::ast::*;
 
use crate::protocol::inputsource::*;
 

	
 
const MAX_LEVEL: usize = 128;
 
const MAX_NAMESPACES: u8 = 8; // only three levels are supported at the moment
 

	
 
macro_rules! debug_log {
 
    ($format:literal) => {
 
        enabled_debug_print!(true, "lexer", $format);
 
    };
 
    ($format:literal, $($args:expr),*) => {
 
        enabled_debug_print!(true, "lexer", $format, $($args),*);
 
    };
 
}
 

	
 
macro_rules! debug_line {
 
    ($source:expr) => {
 
        {
 
            let mut buffer = String::with_capacity(128);
 
            for idx in 0..buffer.capacity() {
 
                let next = $source.lookahead(idx);
 
                if next.is_none() || Some(b'\n') == next { break; }
 
                buffer.push(next.unwrap() as char);
 
            }
 
            buffer
 
        }
 
    };
 
}
 
fn is_vchar(x: Option<u8>) -> bool {
 
    if let Some(c) = x {
 
        c >= 0x21 && c <= 0x7E
 
    } else {
 
        false
 
    }
 
}
 

	
 
fn is_wsp(x: Option<u8>) -> bool {
 
    if let Some(c) = x {
 
        c == b' ' || c == b'\t'
 
    } else {
 
        false
 
    }
 
}
 

	
 
fn is_ident_start(x: Option<u8>) -> bool {
 
    if let Some(c) = x {
 
        c >= b'A' && c <= b'Z' || c >= b'a' && c <= b'z'
 
    } else {
 
        false
 
    }
 
}
 

	
 
fn is_ident_rest(x: Option<u8>) -> bool {
 
    if let Some(c) = x {
 
        c >= b'A' && c <= b'Z' || c >= b'a' && c <= b'z' || c >= b'0' && c <= b'9' || c == b'_'
 
    } else {
 
        false
 
    }
 
}
 

	
 
fn is_constant(x: Option<u8>) -> bool {
 
    if let Some(c) = x {
 
        c >= b'0' && c <= b'9' || c == b'\''
 
    } else {
 
        false
 
    }
 
}
 

	
 
fn is_integer_start(x: Option<u8>) -> bool {
 
    if let Some(c) = x {
 
        c >= b'0' && c <= b'9'
 
    } else {
 
        false
 
    }
 
}
 

	
 
fn is_integer_rest(x: Option<u8>) -> bool {
 
    if let Some(c) = x {
 
        c >= b'0' && c <= b'9'
 
            || c >= b'a' && c <= b'f'
 
            || c >= b'A' && c <= b'F'
 
            || c == b'x'
 
            || c == b'o'
 
    } else {
 
        false
 
    }
 
}
 

	
 
fn lowercase(x: u8) -> u8 {
 
    if x >= b'A' && x <= b'Z' {
 
        x - b'A' + b'a'
 
    } else {
 
        x
 
    }
 
}
 

	
 
fn identifier_as_namespaced(identifier: Identifier) -> NamespacedIdentifier2 {
 
fn identifier_as_namespaced(identifier: Identifier) -> NamespacedIdentifier {
 
    let identifier_len = identifier.value.len();
 
    debug_assert!(identifier_len < u16::max_value() as usize);
 
    NamespacedIdentifier2{
 
    NamespacedIdentifier{
 
        position: identifier.position,
 
        value: identifier.value,
 
        poly_args: Vec::new(),
 
        parts: vec![
 
            NamespacedIdentifierPart::Identifier{start: 0, end: identifier_len as u16}
 
        ],
 
    }
 
}
 

	
 
pub struct Lexer<'a> {
 
    source: &'a mut InputSource,
 
    level: usize,
 
}
 

	
 
impl Lexer<'_> {
 
    pub fn new(source: &mut InputSource) -> Lexer {
 
        Lexer { source, level: 0 }
 
    }
 
    fn error_at_pos(&self, msg: &str) -> ParseError2 {
 
        ParseError2::new_error(self.source, self.source.pos(), msg)
 
    }
 
    fn consume_line(&mut self) -> Result<Vec<u8>, ParseError2> {
 
        let mut result: Vec<u8> = Vec::new();
 
        let mut next = self.source.next();
 
        while next.is_some() && next != Some(b'\n') && next != Some(b'\r') {
 
            if !(is_vchar(next) || is_wsp(next)) {
 
                return Err(self.error_at_pos("Expected visible character or whitespace"));
 
            }
 
            result.push(next.unwrap());
 
            self.source.consume();
 
            next = self.source.next();
 
        }
 
        if next.is_some() {
 
            self.source.consume();
 
        }
 
        if next == Some(b'\r') && self.source.next() == Some(b'\n') {
 
            self.source.consume();
 
        }
 
        Ok(result)
 
    }
 
    fn consume_whitespace(&mut self, expected: bool) -> Result<(), ParseError2> {
 
        let mut found = false;
 
        let mut next = self.source.next();
 
        while next.is_some() {
 
            if next == Some(b' ')
 
                || next == Some(b'\t')
 
                || next == Some(b'\r')
 
                || next == Some(b'\n')
 
            {
 
                self.source.consume();
 
                next = self.source.next();
 
                found = true;
 
                continue;
 
            }
 
            if next == Some(b'/') {
 
                next = self.source.lookahead(1);
 
                if next == Some(b'/') {
 
                    self.source.consume(); // slash
 
                    self.source.consume(); // slash
 
                    self.consume_line()?;
 
                    next = self.source.next();
 
                    found = true;
 
                    continue;
 
                }
 
                if next == Some(b'*') {
 
                    self.source.consume(); // slash
 
                    self.source.consume(); // star
 
                    next = self.source.next();
 
                    while next.is_some() {
 
                        if next == Some(b'*') {
 
                            next = self.source.lookahead(1);
 
                            if next == Some(b'/') {
 
                                self.source.consume(); // star
 
                                self.source.consume(); // slash
 
                                break;
 
                            }
 
                        }
 
                        self.source.consume();
 
                        next = self.source.next();
 
                    }
 
                    next = self.source.next();
 
                    found = true;
 
                    continue;
 
                }
 
            }
 
            break;
 
        }
 
        if expected && !found {
 
            Err(self.error_at_pos("Expected whitespace"))
 
        } else {
 
            Ok(())
 
        }
 
    }
 
    fn consume_any_chars(&mut self) {
 
        if !is_ident_start(self.source.next()) { return }
 
        self.source.consume();
 
@@ -348,473 +348,459 @@ impl Lexer<'_> {
 
        if data_len == 1 {
 
            debug_assert!(data[0] >= b'0' && data[0] <= b'9');
 
            return Ok((data[0] - b'0') as i64);
 
        } else {
 
            // TODO: Fix, u64 should be supported as well
 
            let parsed = if data[1] == b'b' {
 
                let data = String::from_utf8_lossy(&data[2..]);
 
                i64::from_str_radix(&data, 2)
 
            } else if data[1] == b'o' {
 
                let data = String::from_utf8_lossy(&data[2..]);
 
                i64::from_str_radix(&data, 8)
 
            } else if data[1] == b'x' {
 
                let data = String::from_utf8_lossy(&data[2..]);
 
                i64::from_str_radix(&data, 16)
 
            } else {
 
                // Assume decimal
 
                let data = String::from_utf8_lossy(&data);
 
                i64::from_str_radix(&data, 10)
 
            };
 

	
 
            if let Err(_err) = parsed {
 
                return Err(ParseError2::new_error(&self.source, position, "Invalid integer constant"));
 
            }
 

	
 
            Ok(parsed.unwrap())
 
        }
 
    }
 

	
 
    // Statement keywords
 
    // TODO: Clean up these functions
 
    fn has_statement_keyword(&self) -> bool {
 
        self.has_keyword(b"channel")
 
            || self.has_keyword(b"skip")
 
            || self.has_keyword(b"if")
 
            || self.has_keyword(b"while")
 
            || self.has_keyword(b"break")
 
            || self.has_keyword(b"continue")
 
            || self.has_keyword(b"synchronous")
 
            || self.has_keyword(b"return")
 
            || self.has_keyword(b"assert")
 
            || self.has_keyword(b"goto")
 
            || self.has_keyword(b"new")
 
    }
 
    fn has_type_keyword(&self) -> bool {
 
        self.has_keyword(b"in")
 
            || self.has_keyword(b"out")
 
            || self.has_keyword(b"msg")
 
            || self.has_keyword(b"boolean")
 
            || self.has_keyword(b"byte")
 
            || self.has_keyword(b"short")
 
            || self.has_keyword(b"int")
 
            || self.has_keyword(b"long")
 
            || self.has_keyword(b"auto")
 
    }
 
    fn has_builtin_keyword(&self) -> bool {
 
        self.has_keyword(b"get")
 
            || self.has_keyword(b"fires")
 
            || self.has_keyword(b"create")
 
            || self.has_keyword(b"length")
 
    }
 
    fn has_reserved(&self) -> bool {
 
        self.has_statement_keyword()
 
            || self.has_type_keyword()
 
            || self.has_builtin_keyword()
 
            || self.has_keyword(b"let")
 
            || self.has_keyword(b"struct")
 
            || self.has_keyword(b"enum")
 
            || self.has_keyword(b"true")
 
            || self.has_keyword(b"false")
 
            || self.has_keyword(b"null")
 
    }
 

	
 
    // Identifiers
 

	
 
    fn has_identifier(&self) -> bool {
 
        if self.has_statement_keyword() || self.has_type_keyword() || self.has_builtin_keyword() {
 
            return false;
 
        }
 
        let next = self.source.next();
 
        is_ident_start(next)
 
    }
 
    fn consume_identifier(&mut self) -> Result<Identifier, ParseError2> {
 
        if self.has_statement_keyword() || self.has_type_keyword() || self.has_builtin_keyword() {
 
            return Err(self.error_at_pos("Expected identifier"));
 
        }
 
        let position = self.source.pos();
 
        let value = self.consume_ident()?;
 
        Ok(Identifier{ position, value })
 
    }
 
    fn consume_identifier_spilled(&mut self) -> Result<(), ParseError2> {
 
        if self.has_statement_keyword() || self.has_type_keyword() || self.has_builtin_keyword() {
 
            return Err(self.error_at_pos("Expected identifier"));
 
        }
 
        self.consume_ident()?;
 
        Ok(())
 
    }
 
    fn has_namespaced_identifier(&self) -> bool { 
 
        self.has_identifier() 
 
    }
 
    fn consume_namespaced_identifier(&mut self) -> Result<NamespacedIdentifier, ParseError2> {
 
        if self.has_reserved() {
 
            return Err(self.error_at_pos("Encountered reserved keyword"));
 
        }
 

	
 
        let position = self.source.pos();
 
        let mut ns_ident = self.consume_ident()?;
 
        let mut num_namespaces = 1;
 
        while self.has_string(b"::") {
 
            self.consume_string(b"::")?;
 
            if num_namespaces >= MAX_NAMESPACES {
 
                return Err(self.error_at_pos("Too many namespaces in identifier"));
 
            }
 
            let new_ident = self.consume_ident()?;
 
            ns_ident.extend(b"::");
 
            ns_ident.extend(new_ident);
 
            num_namespaces += 1;
 
        }
 

	
 
        Ok(NamespacedIdentifier{
 
            position,
 
            value: ns_ident,
 
            num_namespaces,
 
        })
 
    }
 
    fn consume_namespaced_identifier_spilled(&mut self) -> Result<(), ParseError2> {
 
        // TODO: @performance
 
        if self.has_reserved() {
 
            return Err(self.error_at_pos("Encountered reserved keyword"));
 
        }
 

	
 
        self.consume_ident()?;
 
        while self.has_string(b"::") {
 
            self.consume_string(b"::")?;
 
            self.consume_ident()?;
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    fn consume_namespaced_identifier2(&mut self, h: &mut Heap) -> Result<NamespacedIdentifier2, ParseError2> {
 
    fn consume_namespaced_identifier(&mut self, h: &mut Heap) -> Result<NamespacedIdentifier, ParseError2> {
 
        if self.has_reserved() {
 
            return Err(self.error_at_pos("Encountered reserved keyword"));
 
        }
 

	
 
        // Consumes a part of the namespaced identifier, returns a boolean
 
        // indicating whether polymorphic arguments were specified.
 
        // TODO: Continue here: if we fail to properly parse the polymorphic
 
        //  arguments, assume we have reached the end of the namespaced 
 
        //  identifier and are instead dealing with a less-than operator. Ugly?
 
        //  Yes. Needs tokenizer? Yes. 
 
        fn consume_part(
 
            l: &mut Lexer, h: &mut Heap, ident: &mut NamespacedIdentifier2,
 
            l: &mut Lexer, h: &mut Heap, ident: &mut NamespacedIdentifier,
 
            backup_pos: &mut InputPosition
 
        ) -> Result<(), ParseError2> {
 
            // Consume identifier
 
            if !ident.value.is_empty() {
 
                ident.value.extend(b"::");
 
            }
 
            let ident_start = ident.value.len();
 
            ident.value.extend(l.consume_ident()?);
 
            ident.parts.push(NamespacedIdentifierPart::Identifier{
 
                start: ident_start as u16,
 
                end: ident.value.len() as u16
 
            });
 

	
 
            // Maybe consume polymorphic args.
 
            *backup_pos = l.source.pos();
 
            l.consume_whitespace(false)?;
 
            match l.consume_polymorphic_args(h, true)? {
 
                Some(args) => {
 
                    let poly_start = ident.poly_args.len();
 
                    ident.poly_args.extend(args);
 

	
 
                    ident.parts.push(NamespacedIdentifierPart::PolyArgs{
 
                        start: poly_start as u16,
 
                        end: ident.poly_args.len() as u16,
 
                    });
 

	
 
                    *backup_pos = l.source.pos();
 
                },
 
                None => {}
 
            };
 

	
 
            Ok(())
 
        }
 

	
 
        let mut ident = NamespacedIdentifier2{
 
        let mut ident = NamespacedIdentifier{
 
            position: self.source.pos(),
 
            value: Vec::new(),
 
            poly_args: Vec::new(),
 
            parts: Vec::new(),
 
        };
 

	
 
        // Keep consume parts separted by "::". We don't consume the trailing
 
        // whitespace, hence we keep a backup position at the end of the last
 
        // valid part of the namespaced identifier (i.e. the last ident, or the
 
        // last encountered polymorphic arguments).
 
        let mut backup_pos = self.source.pos();
 
        consume_part(self, h, &mut ident, &mut backup_pos)?;
 
        self.consume_whitespace(false)?;
 
        while self.has_string(b"::") {
 
            self.consume_string(b"::")?;
 
            self.consume_whitespace(false)?;
 
            consume_part(self, h, &mut ident, &mut backup_pos)?;
 
            self.consume_whitespace(false)?;
 
        }
 

	
 
        self.source.seek(backup_pos);
 
        Ok(ident)
 
    }
 

	
 
    fn consume_namespaced_identifier_spilled(&mut self) -> Result<(), ParseError2> {
 
        if self.has_reserved() {
 
            return Err(self.error_at_pos("Encountered reserved keyword"));
 
        }
 

	
 
        debug_log!("consume_nsident2_spilled: {}", debug_line!(self.source));
 

	
 
        fn consume_part_spilled(l: &mut Lexer, backup_pos: &mut InputPosition) -> Result<(), ParseError2> {
 
            l.consume_ident()?;
 
            *backup_pos = l.source.pos();
 
            l.consume_whitespace(false)?;
 
            match l.maybe_consume_poly_args_spilled_without_pos_recovery() {
 
                Ok(true) => { *backup_pos = l.source.pos(); },
 
                Ok(false) => {},
 
                Err(_) => { return Err(l.error_at_pos("Failed to parse poly args (spilled)")) },
 
            }
 
            Ok(())
 
        }
 

	
 
        let mut backup_pos = self.source.pos();
 
        consume_part_spilled(self, &mut backup_pos)?;
 
        self.consume_whitespace(false)?;
 
        while self.has_string(b"::") {
 
            self.consume_string(b"::")?;
 
            self.consume_whitespace(false)?;
 
            consume_part_spilled(self, &mut backup_pos)?;
 
            self.consume_whitespace(false)?;
 
        }
 

	
 
        self.source.seek(backup_pos);
 
        Ok(())
 
    }
 

	
 
    // Types and type annotations
 

	
 
    /// Consumes a type definition. When called the input position should be at
 
    /// the type specification. When done the input position will be at the end
 
    /// of the type specifications (hence may be at whitespace).
 
    fn consume_type2(&mut self, h: &mut Heap, allow_inference: bool) -> Result<ParserTypeId, ParseError2> {
 
        // Small helper function to convert in/out polymorphic arguments. Not
 
        // pretty, but return boolean is true if the error is due to inference
 
        // not being allowed
 
        let reduce_port_poly_args = |
 
            heap: &mut Heap,
 
            port_pos: &InputPosition,
 
            args: Vec<ParserTypeId>,
 
        | -> Result<ParserTypeId, bool> {
 
            match args.len() {
 
                0 => if allow_inference {  
 
                    Ok(heap.alloc_parser_type(|this| ParserType{
 
                        this,
 
                        pos: port_pos.clone(),
 
                        variant: ParserTypeVariant::Inferred
 
                    }))
 
                } else {
 
                    Err(true)
 
                },
 
                1 => Ok(args[0]),
 
                _ => Err(false)
 
            }
 
        };
 

	
 
        // Consume the type
 
        debug_log!("consume_type2: {}", debug_line!(self.source));
 
        let pos = self.source.pos();
 
        let parser_type_variant = if self.has_keyword(b"msg") {
 
            self.consume_keyword(b"msg")?;
 
            ParserTypeVariant::Message
 
        } else if self.has_keyword(b"boolean") {
 
            self.consume_keyword(b"boolean")?;
 
            ParserTypeVariant::Bool
 
        } else if self.has_keyword(b"byte") {
 
            self.consume_keyword(b"byte")?;
 
            ParserTypeVariant::Byte
 
        } else if self.has_keyword(b"short") {
 
            self.consume_keyword(b"short")?;
 
            ParserTypeVariant::Short
 
        } else if self.has_keyword(b"int") {
 
            self.consume_keyword(b"int")?;
 
            ParserTypeVariant::Int
 
        } else if self.has_keyword(b"long") {
 
            self.consume_keyword(b"long")?;
 
            ParserTypeVariant::Long
 
        } else if self.has_keyword(b"str") {
 
            self.consume_keyword(b"str")?;
 
            ParserTypeVariant::String
 
        } else if self.has_keyword(b"auto") {
 
            if !allow_inference {
 
                return Err(ParseError2::new_error(
 
                        &self.source, pos,
 
                        "Type inference is not allowed here"
 
                ));
 
            }
 

	
 
            self.consume_keyword(b"auto")?;
 
            ParserTypeVariant::Inferred
 
        } else if self.has_keyword(b"in") {
 
            // TODO: @cleanup: not particularly neat to have this special case
 
            //  where we enforce polyargs in the parser-phase
 
            self.consume_keyword(b"in")?;
 
            let poly_args = self.consume_polymorphic_args(h, allow_inference)?.unwrap_or_default();
 
            let poly_arg = reduce_port_poly_args(h, &pos, poly_args)
 
                .map_err(|infer_error|  {
 
                    let msg = if infer_error {
 
                        "Type inference is not allowed here"
 
                    } else {
 
                        "Type 'in' only allows for 1 polymorphic argument"
 
                    };
 
                    ParseError2::new_error(&self.source, pos, msg)
 
                })?;
 
            ParserTypeVariant::Input(poly_arg)
 
        } else if self.has_keyword(b"out") {
 
            self.consume_keyword(b"out")?;
 
            let poly_args = self.consume_polymorphic_args(h, allow_inference)?.unwrap_or_default();
 
            let poly_arg = reduce_port_poly_args(h, &pos, poly_args)
 
                .map_err(|infer_error| {
 
                    let msg = if infer_error {
 
                        "Type inference is not allowed here"
 
                    } else {
 
                        "Type 'out' only allows for 1 polymorphic argument, but {} were specified"
 
                    };
 
                    ParseError2::new_error(&self.source, pos, msg)
 
                })?;
 
            ParserTypeVariant::Output(poly_arg)
 
        } else {
 
            // Must be a symbolic type
 
            let identifier = self.consume_namespaced_identifier2(h)?;
 
            let identifier = self.consume_namespaced_identifier(h)?;
 
            ParserTypeVariant::Symbolic(SymbolicParserType{identifier, variant: None, poly_args2: Vec::new()})
 
        };
 

	
 
        // If the type was a basic type (not supporting polymorphic type
 
        // arguments), then we make sure the user did not specify any of them.
 
        let mut backup_pos = self.source.pos();
 
        if !parser_type_variant.supports_polymorphic_args() {
 
            self.consume_whitespace(false)?;
 
            if let Some(b'<') = self.source.next() {
 
                return Err(ParseError2::new_error(
 
                    &self.source, self.source.pos(),
 
                    "This type does not allow polymorphic arguments"
 
                ));
 
            }
 

	
 
            self.source.seek(backup_pos);
 
        }
 

	
 
        let mut parser_type_id = h.alloc_parser_type(|this| ParserType{
 
            this, pos, variant: parser_type_variant
 
        });
 

	
 
        // If we're dealing with arrays, then we need to wrap the currently
 
        // parsed type in array types
 
        self.consume_whitespace(false)?;
 
        while let Some(b'[') = self.source.next() {
 
            let pos = self.source.pos();
 
            self.source.consume();
 
            self.consume_whitespace(false)?;
 
            if let Some(b']') = self.source.next() {
 
                // Type is wrapped in an array
 
                self.source.consume();
 
                parser_type_id = h.alloc_parser_type(|this| ParserType{
 
                    this, pos, variant: ParserTypeVariant::Array(parser_type_id)
 
                });
 
                backup_pos = self.source.pos();
 

	
 
                // In case we're dealing with another array
 
                self.consume_whitespace(false)?;
 
            } else {
 
                return Err(ParseError2::new_error(
 
                    &self.source, pos,
 
                    "Expected a closing ']'"
 
                ));
 
            }
 
        }
 

	
 
        self.source.seek(backup_pos);
 
        Ok(parser_type_id)
 
    }
 

	
 
    /// Attempts to consume a type without returning it. If it doesn't encounter
 
    /// a well-formed type, then the input position is left at a "random"
 
    /// position.
 
    fn maybe_consume_type_spilled_without_pos_recovery(&mut self) -> bool {
 
        // Consume type identifier
 
        debug_log!("maybe_consume_type_spilled_...: {}", debug_line!(self.source));
 
        if self.has_type_keyword() {
 
            self.consume_any_chars();
 
        } else {
 
            let ident = self.consume_namespaced_identifier();
 
            let ident = self.consume_namespaced_identifier_spilled();
 
            if ident.is_err() { return false; }
 
        }
 

	
 
        // Consume any polymorphic arguments that follow the type identifier
 
        let mut backup_pos = self.source.pos();
 
        if self.consume_whitespace(false).is_err() { return false; }
 
        match self.maybe_consume_poly_args_spilled_without_pos_recovery() {
 
            Ok(true) => backup_pos = self.source.pos(),
 
            Ok(false) => {},
 
            Err(()) => return false
 
        }
 
        
 
        // Consume any array specifiers. Make sure we always leave the input
 
        // position at the end of the last array specifier if we do find a
 
        // valid type
 
        if self.consume_whitespace(false).is_err() { return false; }
 
        while let Some(b'[') = self.source.next() {
 
            self.source.consume();
 
            if self.consume_whitespace(false).is_err() { return false; }
 
            if self.source.next() != Some(b']') { return false; }
 
            self.source.consume();
 
            backup_pos = self.source.pos();
 
            if self.consume_whitespace(false).is_err() { return false; }
 
        }
 

	
 
        self.source.seek(backup_pos);
 
        return true;
 
    }
 

	
 
    fn maybe_consume_type_spilled(&mut self) -> bool {
 
        let backup_pos = self.source.pos();
 
        if !self.maybe_consume_type_spilled_without_pos_recovery() {
 
            self.source.seek(backup_pos);
 
            return false;
 
        }
 

	
 
        return true;
 
    }
 

	
 
    /// Attempts to consume polymorphic arguments without returning them. If it
 
    /// doesn't encounter well-formed polymorphic arguments, then the input
 
    /// position is left at a "random" position. Returns a boolean indicating if
 
    /// the poly_args list was present.
 
    fn maybe_consume_poly_args_spilled_without_pos_recovery(&mut self) -> Result<bool, ()> {
 
        debug_log!("maybe_consume_poly_args_spilled_...: {}", debug_line!(self.source));
 
        self.consume_comma_separated_spilled_without_pos_recovery(
 
            b'<', b'>', |lexer| {
 
                lexer.maybe_consume_type_spilled_without_pos_recovery()
 
            })
 
    }
 

	
 
    /// Consumes polymorphic arguments and its delimiters if specified. If
 
    /// polyargs are present then the args are consumed and the input position
 
    /// will be placed after the polyarg list. If polyargs are not present then
 
    /// the input position will remain unmodified and an empty vector will be
 
    /// returned.
 
    ///
 
    /// Polymorphic arguments represent the specification of the parametric
 
    /// types of a polymorphic type: they specify the value of the polymorphic
 
    /// type's polymorphic variables.
 
    fn consume_polymorphic_args(&mut self, h: &mut Heap, allow_inference: bool) -> Result<Option<Vec<ParserTypeId>>, ParseError2> {
 
        self.consume_comma_separated(
 
            h, b'<', b'>', "Expected the end of the polymorphic argument list",
 
            |lexer, heap| lexer.consume_type2(heap, allow_inference)
 
        )
 
    }
 

	
 
    /// Consumes polymorphic variables. These are identifiers that are used
 
    /// within polymorphic types. The input position may be at whitespace. If
 
    /// polymorphic variables are present then the whitespace, wrapping
 
    /// delimiters and the polymorphic variables are consumed. Otherwise the
 
    /// input position will stay where it is. If no polymorphic variables are
 
    /// present then an empty vector will be returned.
 
    fn consume_polymorphic_vars(&mut self, h: &mut Heap) -> Result<Vec<Identifier>, ParseError2> {
 
        let backup_pos = self.source.pos();
 
        match self.consume_comma_separated(
 
            h, b'<', b'>', "Expected the end of the polymorphic variable list",
 
            |lexer, _heap| lexer.consume_identifier()
 
        )? {
 
            Some(poly_vars) => Ok(poly_vars),
 
            None => {
 
                self.source.seek(backup_pos);
 
                Ok(vec!())
 
            }
 
        }
 
    }
 

	
 
    // Parameters
 

	
 
    fn consume_parameter(&mut self, h: &mut Heap) -> Result<ParameterId, ParseError2> {
 
        let parser_type = self.consume_type2(h, false)?;
 
        self.consume_whitespace(true)?;
 
        let position = self.source.pos();
 
        let identifier = self.consume_identifier()?;
 
        let id =
 
            h.alloc_parameter(|this| Parameter { this, position, parser_type, identifier });
 
        Ok(id)
 
    }
 
    fn consume_parameters(&mut self, h: &mut Heap) -> Result<Vec<ParameterId>, ParseError2> {
 
        match self.consume_comma_separated(
 
            h, b'(', b')', "Expected the end of the parameter list",
 
            |lexer, heap| lexer.consume_parameter(heap)
 
        )? {
 
            Some(params) => Ok(params),
 
            None => {
 
                Err(ParseError2::new_error(
 
                    &self.source, self.source.pos(),
 
@@ -1442,286 +1428,282 @@ impl Lexer<'_> {
 
            return Ok(self.consume_array_expression(h)?.upcast());
 
        }
 
        if self.has_builtin_literal() {
 
            return Ok(self.consume_builtin_literal_expression(h)?.upcast());
 
        }
 
        if self.has_struct_literal() {
 
            return Ok(self.consume_struct_literal_expression(h)?.upcast());
 
        }
 
        if self.has_call_expression() {
 
            return Ok(self.consume_call_expression(h)?.upcast());
 
        }
 
        Ok(self.consume_variable_expression(h)?.upcast())
 
    }
 
    fn consume_array_expression(&mut self, h: &mut Heap) -> Result<ArrayExpressionId, ParseError2> {
 
        let position = self.source.pos();
 
        let mut elements = Vec::new();
 
        self.consume_string(b"{")?;
 
        self.consume_whitespace(false)?;
 
        if !self.has_string(b"}") {
 
            while self.source.next().is_some() {
 
                elements.push(self.consume_expression(h)?);
 
                self.consume_whitespace(false)?;
 
                if self.has_string(b"}") {
 
                    break;
 
                }
 
                self.consume_string(b",")?;
 
                self.consume_whitespace(false)?;
 
            }
 
        }
 
        self.consume_string(b"}")?;
 
        Ok(h.alloc_array_expression(|this| ArrayExpression {
 
            this,
 
            position,
 
            elements,
 
            parent: ExpressionParent::None,
 
            concrete_type: ConcreteType::default(),
 
        }))
 
    }
 
    fn has_builtin_literal(&self) -> bool {
 
        is_constant(self.source.next())
 
            || self.has_keyword(b"null")
 
            || self.has_keyword(b"true")
 
            || self.has_keyword(b"false")
 
    }
 
    fn consume_builtin_literal_expression(
 
        &mut self,
 
        h: &mut Heap,
 
    ) -> Result<LiteralExpressionId, ParseError2> {
 
        let position = self.source.pos();
 
        let value;
 
        if self.has_keyword(b"null") {
 
            self.consume_keyword(b"null")?;
 
            value = Literal::Null;
 
        } else if self.has_keyword(b"true") {
 
            self.consume_keyword(b"true")?;
 
            value = Literal::True;
 
        } else if self.has_keyword(b"false") {
 
            self.consume_keyword(b"false")?;
 
            value = Literal::False;
 
        } else if self.source.next() == Some(b'\'') {
 
            self.source.consume();
 
            let mut data = Vec::new();
 
            let mut next = self.source.next();
 
            while next != Some(b'\'') && (is_vchar(next) || next == Some(b' ')) {
 
                data.push(next.unwrap());
 
                self.source.consume();
 
                next = self.source.next();
 
            }
 
            if next != Some(b'\'') || data.is_empty() {
 
                return Err(self.error_at_pos("Expected character constant"));
 
            }
 
            self.source.consume();
 
            value = Literal::Character(data);
 
        } else {
 
            if !self.has_integer() {
 
                return Err(self.error_at_pos("Expected integer constant"));
 
            }
 

	
 
            value = Literal::Integer(self.consume_integer()?);
 
        }
 
        Ok(h.alloc_literal_expression(|this| LiteralExpression {
 
            this,
 
            position,
 
            value,
 
            parent: ExpressionParent::None,
 
            concrete_type: ConcreteType::default(),
 
        }))
 
    }
 

	
 
    fn has_struct_literal(&mut self) -> bool {
 
        // A struct literal is written as:
 
        //      namespace::StructName<maybe_one_of_these, auto>{ field: expr }
 
        // We will parse up until the opening brace to see if we're dealing with
 
        // a struct literal.
 
        let backup_pos = self.source.pos();
 
        let result = self.consume_namespaced_identifier_spilled().is_ok() &&
 
            self.consume_whitespace(false).is_ok() &&
 
            self.maybe_consume_poly_args_spilled_without_pos_recovery().is_ok() &&
 
            self.consume_whitespace(false).is_ok() &&
 
            self.source.next() == Some(b'{');
 

	
 
        self.source.seek(backup_pos);
 
        return result;
 
    }
 

	
 
    fn consume_struct_literal_expression(&mut self, h: &mut Heap) -> Result<LiteralExpressionId, ParseError2> {
 
        // Consume identifier and polymorphic arguments
 
        debug_log!("consume_struct_literal_expression: {}", debug_line!(self.source));
 
        let position = self.source.pos();
 
        let identifier = self.consume_namespaced_identifier2(h)?;
 
        let identifier = self.consume_namespaced_identifier(h)?;
 
        self.consume_whitespace(false)?;
 

	
 
        // Consume fields
 
        let fields = match self.consume_comma_separated(
 
            h, b'{', b'}', "Expected the end of the list of struct fields",
 
            |lexer, heap| {
 
                let identifier = lexer.consume_identifier()?;
 
                lexer.consume_whitespace(false)?;
 
                lexer.consume_string(b":")?;
 
                lexer.consume_whitespace(false)?;
 
                let value = lexer.consume_expression(heap)?;
 

	
 
                Ok(LiteralStructField{ identifier, value, field_idx: 0 })
 
            }
 
        )? {
 
            Some(fields) => fields,
 
            None => return Err(ParseError2::new_error(
 
                self.source, self.source.pos(),
 
                "A struct literal must be followed by its field values"
 
            ))
 
        };
 

	
 
        Ok(h.alloc_literal_expression(|this| LiteralExpression{
 
            this,
 
            position,
 
            value: Literal::Struct(LiteralStruct{
 
                identifier,
 
                fields,
 
                poly_args2: Vec::new(),
 
                definition: None,
 
            }),
 
            parent: ExpressionParent::None,
 
            concrete_type: Default::default()
 
        }))
 
    }
 

	
 
    fn has_call_expression(&mut self) -> bool {
 
        // We need to prevent ambiguity with various operators (because we may
 
        // be specifying polymorphic variables) and variables.
 
        if self.has_builtin_keyword() {
 
            return true;
 
        }
 

	
 
        let backup_pos = self.source.pos();
 
        let mut result = false;
 

	
 
        if self.consume_namespaced_identifier_spilled().is_ok() &&
 
            self.consume_whitespace(false).is_ok() &&
 
            self.maybe_consume_poly_args_spilled_without_pos_recovery().is_ok() &&
 
            self.consume_whitespace(false).is_ok() &&
 
            self.source.next() == Some(b'(') {
 
            // Seems like we have a function call or an enum literal
 
            result = true;
 
        }
 

	
 
        self.source.seek(backup_pos);
 
        return result;
 
    }
 
    fn consume_call_expression(&mut self, h: &mut Heap) -> Result<CallExpressionId, ParseError2> {
 
        let position = self.source.pos();
 

	
 
        // Consume method identifier
 
        // TODO: @token Replace this conditional polymorphic arg parsing once we have a tokenizer.
 
        debug_log!("consume_call_expression: {}", debug_line!(self.source));
 
        let method;
 
        let mut consume_poly_args_explicitly = true;
 
        if self.has_keyword(b"get") {
 
            self.consume_keyword(b"get")?;
 
            method = Method::Get;
 
        } else if self.has_keyword(b"put") {
 
            self.consume_keyword(b"put")?;
 
            method = Method::Put;
 
        } else if self.has_keyword(b"fires") {
 
            self.consume_keyword(b"fires")?;
 
            method = Method::Fires;
 
        } else if self.has_keyword(b"create") {
 
            self.consume_keyword(b"create")?;
 
            method = Method::Create;
 
        } else {
 
            let identifier = self.consume_namespaced_identifier2(h)?;
 
            let identifier = self.consume_namespaced_identifier(h)?;
 
            method = Method::Symbolic(MethodSymbolic{
 
                identifier,
 
                definition: None
 
            });
 
            consume_poly_args_explicitly = false;
 
        };
 

	
 
        // Consume polymorphic arguments
 
        let poly_args = if consume_poly_args_explicitly {
 
            self.consume_whitespace(false)?;
 
            self.consume_polymorphic_args(h, true)?.unwrap_or_default()
 
        } else {
 
            Vec::new()
 
        };
 

	
 
        // Consume arguments to call
 
        self.consume_whitespace(false)?;
 
        let mut arguments = Vec::new();
 
        self.consume_string(b"(")?;
 
        self.consume_whitespace(false)?;
 
        if !self.has_string(b")") {
 
            // TODO: allow trailing comma
 
            while self.source.next().is_some() {
 
                arguments.push(self.consume_expression(h)?);
 
                self.consume_whitespace(false)?;
 
                if self.has_string(b")") {
 
                    break;
 
                }
 
                self.consume_string(b",")?;
 
                self.consume_whitespace(false)?
 
            }
 
        }
 
        self.consume_string(b")")?;
 
        Ok(h.alloc_call_expression(|this| CallExpression {
 
            this,
 
            position,
 
            method,
 
            arguments,
 
            poly_args,
 
            parent: ExpressionParent::None,
 
            concrete_type: ConcreteType::default(),
 
        }))
 
    }
 
    fn consume_variable_expression(
 
        &mut self,
 
        h: &mut Heap,
 
    ) -> Result<VariableExpressionId, ParseError2> {
 
        let position = self.source.pos();
 
        debug_log!("consume_variable_expression: {}", debug_line!(self.source));
 

	
 
        // TODO: @token Reimplement when tokenizer is implemented, prevent ambiguities
 
        let identifier = identifier_as_namespaced(self.consume_identifier()?);
 

	
 
        Ok(h.alloc_variable_expression(|this| VariableExpression {
 
            this,
 
            position,
 
            identifier,
 
            declaration: None,
 
            parent: ExpressionParent::None,
 
            concrete_type: ConcreteType::default(),
 
        }))
 
    }
 

	
 
    // ====================
 
    // Statements
 
    // ====================
 

	
 
    /// Consumes any kind of statement from the source and will error if it
 
    /// did not encounter a statement. Will also return an error if the
 
    /// statement is nested too deeply.
 
    ///
 
    /// `wrap_in_block` may be set to true to ensure that the parsed statement
 
    /// will be wrapped in a block statement if it is not already a block
 
    /// statement. This is used to ensure that all `if`, `while` and `sync`
 
    /// statements have a block statement as body.
 
    fn consume_statement(&mut self, h: &mut Heap, wrap_in_block: bool) -> Result<StatementId, ParseError2> {
 
        if self.level >= MAX_LEVEL {
 
            return Err(self.error_at_pos("Too deeply nested statement"));
 
        }
 
        self.level += 1;
 
        let result = self.consume_statement_impl(h, wrap_in_block);
 
        self.level -= 1;
 
        result
 
    }
 
    fn has_label(&mut self) -> bool {
 
        // To prevent ambiguity with expression statements consisting only of an
 
        // identifier or a namespaced identifier, we look ahead and match on the
 
        // *single* colon that signals a labeled statement.
 
        let backup_pos = self.source.pos();
 
        let mut result = false;
 
        if self.consume_identifier_spilled().is_ok() {
 
            // next character is ':', second character is NOT ':'
 
            result = Some(b':') == self.source.next() && Some(b':') != self.source.lookahead(1)
 
        }
 
        self.source.seek(backup_pos);
 
        return result;
src/protocol/parser/symbol_table.rs
Show inline comments
 
// TODO: Maybe allow namespaced-aliased imports. It is currently not possible
 
//  to express the following:
 
//      import Module.Submodule as SubMod
 
//      import SubMod::{Symbol}
 
//  And it is especially not possible to express the following:
 
//      import SubMod::{Symbol}
 
//      import Module.Submodule as SubMod
 
use crate::protocol::ast::*;
 
use crate::protocol::inputsource::*;
 

	
 
use std::collections::{HashMap, hash_map::Entry};
 
use crate::protocol::parser::LexedModule;
 

	
 
#[derive(PartialEq, Eq, Hash)]
 
struct SymbolKey {
 
    module_id: RootId,
 
    symbol_name: Vec<u8>,
 
}
 

	
 
impl SymbolKey {
 
    fn from_identifier(module_id: RootId, symbol: &Identifier) -> Self {
 
        Self{ module_id, symbol_name: symbol.value.clone() }
 
    }
 

	
 
    fn from_namespaced_identifier(module_id: RootId, symbol: &NamespacedIdentifier2) -> Self {
 
    fn from_namespaced_identifier(module_id: RootId, symbol: &NamespacedIdentifier) -> Self {
 
        Self{ module_id, symbol_name: symbol.strip_poly_args() }
 
    }
 
}
 

	
 
pub(crate) enum Symbol {
 
    Namespace(RootId),
 
    Definition((RootId, DefinitionId)),
 
}
 

	
 
pub(crate) struct SymbolValue {
 
    // Position is the place where the symbol is introduced to a module (this
 
    // position always corresponds to the module whose RootId is stored in the
 
    // `SymbolKey` associated with this `SymbolValue`). For a definition this
 
    // is the position where the symbol is defined, for an import this is the
 
    // position of the import statement.
 
    pub(crate) position: InputPosition,
 
    pub(crate) symbol: Symbol,
 
}
 

	
 
impl SymbolValue {
 
    pub(crate) fn is_namespace(&self) -> bool {
 
        match &self.symbol {
 
            Symbol::Namespace(_) => true,
 
            _ => false
 
        }
 
    }
 
    pub(crate) fn as_namespace(&self) -> Option<RootId> {
 
        match &self.symbol {
 
            Symbol::Namespace(root_id) => Some(*root_id),
 
            _ => None,
 
        }
 
    }
 

	
 
    pub(crate) fn as_definition(&self) -> Option<(RootId, DefinitionId)> {
 
        match &self.symbol {
 
            Symbol::Definition((root_id, definition_id)) => Some((*root_id, *definition_id)),
 
            _ => None,
 
        }
 
    }
 
}
 
/// `SymbolTable` is responsible for two parts of the parsing process: firstly
 
/// it ensures that there are no clashing symbol definitions within each file,
 
/// and secondly it will resolve all symbols within a module to their
 
/// appropriate definitions (in case of enums, functions, etc.) and namespaces
 
/// (currently only external modules can act as namespaces). If a symbol clashes
 
/// or if a symbol cannot be resolved this will be an error.
 
///
 
/// Within the compilation process the symbol table is responsible for resolving
 
/// namespaced identifiers (e.g. Module::Enum::EnumVariant) to the appropriate
 
/// definition (i.e. not namespaces; as the language has no way to use
 
/// namespaces except for using them in namespaced identifiers).
 
pub(crate) struct SymbolTable {
 
    // Lookup from module name (not any aliases) to the root id
 
    module_lookup: HashMap<Vec<u8>, RootId>,
 
    // Lookup from within a module, to a particular imported (potentially
 
    // aliased) or defined symbol. Basically speaking: if the source code of a
 
    // module contains correctly imported/defined symbols, then this lookup
 
    // will always return the corresponding definition
 
    symbol_lookup: HashMap<SymbolKey, SymbolValue>,
 
}
 

	
 
impl SymbolTable {
 
    pub(crate) fn new() -> Self {
 
        Self{ module_lookup: HashMap::new(), symbol_lookup: HashMap::new() }
 
    }
 

	
 
    pub(crate) fn build(&mut self, heap: &Heap, modules: &[LexedModule]) -> Result<(), ParseError2> {
 
        // Sanity checks
 
        debug_assert!(self.module_lookup.is_empty());
 
        debug_assert!(self.symbol_lookup.is_empty());
 
        if cfg!(debug_assertions) {
 
            for (index, module) in modules.iter().enumerate() {
 
                debug_assert_eq!(
 
                    index, module.root_id.index as usize,
 
                    "module RootId does not correspond to LexedModule index"
 
                )
 
            }
 
        }
 

	
 
        // Preparation: create a lookup from module name to root id. This does
 
        // not take aliasing into account.
 
        self.module_lookup.reserve(modules.len());
 
        for module in modules {
 
            // TODO: Maybe put duplicate module name checking here?
 
            // TODO: @string
 
            self.module_lookup.insert(module.module_name.clone(), module.root_id);
 
        }
 

	
 
        // Preparation: determine total number of imports we will be inserting
 
        // into the lookup table. We could just iterate over the arena, but then
 
        // we don't know the source file the import belongs to.
 
        let mut lookup_reserve_size = 0;
 
        for module in modules {
 
            let module_root = &heap[module.root_id];
 
            for import_id in &module_root.imports {
 
                match &heap[*import_id] {
 
@@ -242,194 +242,194 @@ impl SymbolTable {
 
                                // to "import a module's imported symbol". And so if we do find
 
                                // a symbol match, we need to make sure it is a definition from
 
                                // within that module by checking `source_root_id == target_root_id`
 
                                let key = SymbolKey::from_identifier(target_root_id, &symbol.name);
 
                                let target_symbol = self.symbol_lookup.get(&key);
 
                                let symbol_definition_id = match target_symbol {
 
                                    Some(target_symbol) => {
 
                                        match target_symbol.symbol {
 
                                            Symbol::Definition((symbol_root_id, symbol_definition_id)) => {
 
                                                if symbol_root_id == target_root_id {
 
                                                    Some(symbol_definition_id)
 
                                                } else {
 
                                                    // This is imported within the target module, and not
 
                                                    // defined within the target module
 
                                                    None
 
                                                }
 
                                            },
 
                                            Symbol::Namespace(_) => {
 
                                                // We don't import a module's "module import"
 
                                                None
 
                                            }
 
                                        }
 
                                    },
 
                                    None => None
 
                                };
 

	
 
                                if symbol_definition_id.is_none() {
 
                                    return Err(
 
                                        ParseError2::new_error(&module.source, symbol.position, "Could not resolve symbol")
 
                                    )
 
                                }
 
                                let symbol_definition_id = symbol_definition_id.unwrap();
 

	
 
                                if let Err(previous_position) = self.add_definition_symbol(
 
                                    symbol.position, SymbolKey::from_identifier(module.root_id, &symbol.alias),
 
                                    target_root_id, symbol_definition_id
 
                                ) {
 
                                    return Err(
 
                                        ParseError2::new_error(&module.source, symbol.position, "Symbol is multiply defined")
 
                                            .with_postfixed_info(&module.source, previous_position, "Previous definition was here")
 
                                    )
 
                                }
 
                            }
 
                        }
 
                    }
 
                }
 
            }
 
        }
 
        fn find_name(heap: &Heap, root_id: RootId) -> String {
 
            let root = &heap[root_id];
 
            for pragma_id in &root.pragmas {
 
                match &heap[*pragma_id] {
 
                    Pragma::Module(module) => {
 
                        return String::from_utf8_lossy(&module.value).to_string()
 
                    },
 
                    _ => {},
 
                }
 
            }
 

	
 
            return String::from("Unknown")
 
        }
 

	
 
        debug_assert_eq!(
 
            self.symbol_lookup.len(), lookup_reserve_size,
 
            "miscalculated reserved size for symbol lookup table"
 
        );
 
        Ok(())
 
    }
 

	
 
    /// Resolves a module by its defined name
 
    pub(crate) fn resolve_module(&self, identifier: &Vec<u8>) -> Option<RootId> {
 
        self.module_lookup.get(identifier).map(|v| *v)
 
    }
 

	
 
    pub(crate) fn resolve_symbol<'t>(
 
        &'t self, root_module_id: RootId, identifier: &[u8]
 
    ) -> Option<&'t SymbolValue> {
 
        let lookup_key = SymbolKey{ module_id: root_module_id, symbol_name: Vec::from(identifier) };
 
        self.symbol_lookup.get(&lookup_key)
 
    }
 

	
 
    pub(crate) fn resolve_identifier<'t>(
 
        &'t self, root_module_id: RootId, identifier: &Identifier
 
    ) -> Option<&'t SymbolValue> {
 
        let lookup_key = SymbolKey::from_identifier(root_module_id, identifier);
 
        self.symbol_lookup.get(&lookup_key)
 
    }
 

	
 
    /// Resolves a namespaced symbol. This method will go as far as possible in
 
    /// going to the right symbol. It will halt the search when:
 
    /// 1. Polymorphic arguments are encountered on the identifier.
 
    /// 2. A non-namespace symbol is encountered.
 
    /// 3. A part of the identifier couldn't be resolved to anything
 
    /// The returned iterator will always point to the next symbol (even if 
 
    /// nothing was found)
 
    pub(crate) fn resolve_namespaced_identifier<'t, 'i>(
 
        &'t self, root_module_id: RootId, identifier: &'i NamespacedIdentifier2
 
    ) -> (Option<&'t SymbolValue>, NamespacedIdentifier2Iter<'i>) {
 
        &'t self, root_module_id: RootId, identifier: &'i NamespacedIdentifier
 
    ) -> (Option<&'t SymbolValue>, NamespacedIdentifierIter<'i>) {
 
        let mut iter = identifier.iter();
 
        let mut symbol: Option<&SymbolValue> = None;
 
        let mut within_module_id = root_module_id;
 

	
 
        while let Some((partial, poly_args)) = iter.next() {
 
            // Lookup the symbol within the currently iterated upon module
 
            let lookup_key = SymbolKey{ module_id: within_module_id, symbol_name: Vec::from(partial) };
 
            let new_symbol = self.symbol_lookup.get(&lookup_key);
 
            
 
            match new_symbol {
 
                None => {
 
                    // Can't find anything
 
                    symbol = None;
 
                    break;
 
                },
 
                Some(new_symbol) => {
 
                    // Found something, but if we already moved to another
 
                    // module then we don't want to keep jumping across modules,
 
                    // we're only interested in symbols defined within that
 
                    // module.
 
                    match &new_symbol.symbol {
 
                        Symbol::Namespace(new_root_id) => {
 
                            if root_module_id != within_module_id {
 
                                // This new symbol is imported by a foreign
 
                                // module, so this is an error
 
                                debug_assert!(symbol.is_some());
 
                                debug_assert!(symbol.unwrap().is_namespace());
 
                                debug_assert!(iter.num_returned() > 1);
 
                                symbol = None;
 
                                break;
 
                            }
 
                            within_module_id = *new_root_id;
 
                            symbol = Some(new_symbol);
 
                        },
 
                        Symbol::Definition((definition_root_id, _)) => {
 
                            // Found a definition, but if we already jumped
 
                            // modules, then this must be defined within that
 
                            // module.
 
                            if root_module_id != within_module_id && within_module_id != *definition_root_id {
 
                                // This is an imported definition within the module
 
                                // So keep the old 
 
                                debug_assert!(symbol.is_some());
 
                                debug_assert!(symbol.unwrap().is_namespace());
 
                                debug_assert!(iter.num_returned() > 1);
 
                                symbol = None;
 
                                break;
 
                            }
 
                            symbol = Some(new_symbol);
 
                            break;
 
                        }
 
                    }
 
                }
 
            }
 

	
 
            if poly_args.is_some() {
 
                // Polymorphic argument specification should also be a fully 
 
                // resolved result.
 
                break;
 
            }
 
        }
 

	
 
        match symbol {
 
            None => (None, iter),
 
            Some(symbol) => (Some(symbol), iter)
 
        }
 
    }
 

	
 
    /// Attempts to add a namespace symbol. Returns `Ok` if the symbol was
 
    /// inserted. If the symbol already exists then `Err` will be returned
 
    /// together with the previous definition's source position (in the origin
 
    /// module's source file).
 
    // Note: I would love to return a reference to the value, but Rust is
 
    // preventing me from doing so... That, or I'm not smart enough...
 
    fn add_namespace_symbol(
 
        &mut self, origin_position: InputPosition, key: SymbolKey, target_module_id: RootId
 
    ) -> Result<(), InputPosition> {
 
        match self.symbol_lookup.entry(key) {
 
            Entry::Occupied(o) => Err(o.get().position),
 
            Entry::Vacant(v) => {
 
                v.insert(SymbolValue{
 
                    position: origin_position,
 
                    symbol: Symbol::Namespace(target_module_id)
 
                });
 
                Ok(())
 
            }
 
        }
 
    }
 

	
 
    /// Attempts to add a definition symbol. Returns `Ok` if the symbol was
 
    /// inserted. If the symbol already exists then `Err` will be returned
 
    /// together with the previous definition's source position (in the origin
 
    /// module's source file).
 
    fn add_definition_symbol(
 
        &mut self, origin_position: InputPosition, key: SymbolKey,
 
        target_module_id: RootId, target_definition_id: DefinitionId,
 
    ) -> Result<(), InputPosition> {
src/protocol/parser/utils.rs
Show inline comments
 
    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, NamespacedIdentifier2Iter<'i>)),
 
    Found((&'t DefinedType, NamespacedIdentifierIter<'i>)),
 
    // Could not match symbol
 
    SymbolNotFound{ident_pos: InputPosition},
 
    // Matched part of the namespaced identifier, but not completely
 
    SymbolPartial{ident_pos: InputPosition, ident_iter: NamespacedIdentifier2Iter<'i>},
 
    SymbolPartial{ident_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},
 
}
 

	
 
// TODO: @cleanup Find other uses of this pattern
 
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, NamespacedIdentifier2Iter<'i>), ParseError2> {
 
    pub(crate) fn as_parse_error(self, module_source: &InputSource) -> Result<(&'t DefinedType, NamespacedIdentifierIter<'i>), 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, ident_iter} => {
 
                Err(ParseError2::new_error(
 
                    module_source, ident_pos, 
 
                    &format!(
 
                        "Could not fully resolve this identifier to a symbol, was only able to match '{}'",
 
                        &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 NamespacedIdentifier2
 
    root_id: RootId, identifier: &'i NamespacedIdentifier
 
) -> FindTypeResult<'t, 'i> {
 
    // Lookup symbol
 
    let (symbol, ident_iter) = symbols.resolve_namespaced_identifier(root_id, identifier);
 
    if symbol.is_none() { 
 
        return FindTypeResult::SymbolNotFound{ident_pos: identifier.position};
 
    }
 
    
 
    // Make sure we resolved it exactly
 
    let symbol = symbol.unwrap();
 
    if ident_iter.num_remaining() != 0 { 
 
        return FindTypeResult::SymbolPartial{
 
            ident_pos: identifier.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(), ident_iter))
 
        }
 
    }
 
}
 

	
 
pub(crate) enum MatchPolymorphResult<'t> {
 
    Matching,
 
    InferAll(usize),
 
    Mismatch{defined_type: &'t DefinedType, ident_position: InputPosition, num_specified: usize},
 
    NoneExpected{defined_type: &'t DefinedType, ident_position: InputPosition, num_specified: usize},
 
    NoneExpected{defined_type: &'t DefinedType, ident_position: InputPosition},
 
}
 

	
 
impl<'t> MatchPolymorphResult<'t> {
 
    pub(crate) fn as_parse_error(self, heap: &Heap, module_source: &InputSource) -> Result<usize, ParseError2> {
 
        match self {
 
            MatchPolymorphResult::Matching => Ok(0),
 
            MatchPolymorphResult::InferAll(count) => {
 
                debug_assert!(count > 0);
 
                Ok(count)
 
            },
 
            MatchPolymorphResult::Mismatch{defined_type, ident_position, num_specified} => {
 
                let type_identifier = heap[defined_type.ast_definition].identifier();
 
                let args_name = if defined_type.poly_vars.len() == 1 {
 
                    "argument"
 
                } else {
 
                    "arguments"
 
                };
 

	
 
                return Err(ParseError2::new_error(
 
                    module_source, ident_position,
 
                    &format!(
 
                        "expected {} polymorphic {} (or none, to infer them) for the type {}, but {} were specified",
 
                        defined_type.poly_vars.len(), args_name, 
 
                        &String::from_utf8_lossy(&type_identifier.value),
 
                        num_specified
 
                    )
 
                ))
 
            },
 
            MatchPolymorphResult::NoneExpected{defined_type, ident_position, ..} => {
 
                let type_identifier = heap[defined_type.ast_definition].identifier();
 
                return Err(ParseError2::new_error(
 
                    module_source, ident_position,
 
                    &format!(
 
                        "the type {} is not polymorphic",
 
                        &String::from_utf8_lossy(&type_identifier.value)
 
                    )
 
                ))
 
            }
 
        }
 
    }
 
}
 

	
 
/// Attempt to match the polymorphic arguments to the number of polymorphic
 
/// variables in the definition.
 
pub(crate) fn match_polymorphic_args_to_vars<'t>(
 
    defined_type: &'t DefinedType, poly_args: Option<&[ParserTypeId]>, ident_position: InputPosition
 
) -> MatchPolymorphResult<'t> {
 
    if defined_type.poly_vars.is_empty() {
 
        // No polymorphic variables on type
 
        if poly_args.is_some() {
 
            return MatchPolymorphResult::NoneExpected{
 
                defined_type,
 
                ident_position, 
 
                num_specified: poly_args.unwrap().len()};
 
            };
 
        }
 
    } else {
 
        // Polymorphic variables on type
 
        let has_specified = poly_args.map_or(false, |a| a.len() != 0);
 
        if !has_specified {
 
            // Implicitly infer all of the polymorphic arguments
 
            return MatchPolymorphResult::InferAll(defined_type.poly_vars.len());
 
        }
 

	
 
        let num_specified = poly_args.unwrap().len();
 
        if num_specified != defined_type.poly_vars.len() {
 
            return MatchPolymorphResult::Mismatch{
 
                defined_type,
 
                ident_position,
 
                num_specified,
 
            };
 
        }
 
    }
 

	
 
    MatchPolymorphResult::Matching
 
}
 
\ No newline at end of file
src/protocol/parser/visitor_linker.rs
Show inline comments
 
@@ -1146,327 +1146,327 @@ impl ValidityAndLinkerVisitor {
 
                if num_inferred_to_allocate != 0 {
 
                    symbolic.poly_args2.reserve(num_inferred_to_allocate);
 
                    for _ in 0..num_inferred_to_allocate {
 
                        symbolic.poly_args2.push(self.parser_type_buffer.pop().unwrap());
 
                    }
 
                } else if !symbolic.identifier.poly_args.is_empty() {
 
                    symbolic.poly_args2.extend(&symbolic.identifier.poly_args)
 
                }
 
                symbolic.variant = Some(symbolic_variant);
 
            } else {
 
                unreachable!();
 
            }
 
        }
 

	
 
        Ok(())
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Utilities
 
    //--------------------------------------------------------------------------
 

	
 
    /// Adds a local variable to the current scope. It will also annotate the
 
    /// `Local` in the AST with its relative position in the block.
 
    fn checked_local_add(&mut self, ctx: &mut Ctx, relative_pos: u32, id: LocalId) -> Result<(), ParseError2> {
 
        debug_assert!(self.cur_scope.is_some());
 

	
 
        // Make sure we do not conflict with any global symbols
 
        {
 
            let ident = &ctx.heap[id].identifier;
 
            if let Some(symbol) = ctx.symbols.resolve_symbol(ctx.module.root_id, &ident.value) {
 
                return Err(
 
                    ParseError2::new_error(&ctx.module.source, ident.position, "Local variable declaration conflicts with symbol")
 
                        .with_postfixed_info(&ctx.module.source, symbol.position, "Conflicting symbol is found here")
 
                );
 
            }
 
        }
 

	
 
        let local = &mut ctx.heap[id];
 
        local.relative_pos_in_block = relative_pos;
 

	
 
        // Make sure we do not shadow any variables in any of the scopes. Note
 
        // that variables in parent scopes may be declared later
 
        let local = &ctx.heap[id];
 
        let mut scope = self.cur_scope.as_ref().unwrap();
 
        let mut local_relative_pos = self.relative_pos_in_block;
 

	
 
        loop {
 
            debug_assert!(scope.is_block(), "scope is not a block");
 
            let block = &ctx.heap[scope.to_block()];
 
            for other_local_id in &block.locals {
 
                let other_local = &ctx.heap[*other_local_id];
 
                // Position check in case another variable with the same name
 
                // is defined in a higher-level scope, but later than the scope
 
                // in which the current variable resides.
 
                if local.this != *other_local_id &&
 
                    local_relative_pos >= other_local.relative_pos_in_block &&
 
                    local.identifier == other_local.identifier {
 
                    // Collision within this scope
 
                    return Err(
 
                        ParseError2::new_error(&ctx.module.source, local.position, "Local variable name conflicts with another variable")
 
                            .with_postfixed_info(&ctx.module.source, other_local.position, "Previous variable is found here")
 
                    );
 
                }
 
            }
 

	
 
            // Current scope is fine, move to parent scope if any
 
            debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent");
 
            scope = block.parent_scope.as_ref().unwrap();
 
            if let Scope::Definition(definition_id) = scope {
 
                // At outer scope, check parameters of function/component
 
                for parameter_id in ctx.heap[*definition_id].parameters() {
 
                    let parameter = &ctx.heap[*parameter_id];
 
                    if local.identifier == parameter.identifier {
 
                        return Err(
 
                            ParseError2::new_error(&ctx.module.source, local.position, "Local variable name conflicts with parameter")
 
                                .with_postfixed_info(&ctx.module.source, parameter.position, "Parameter definition is found here")
 
                        );
 
                    }
 
                }
 

	
 
                break;
 
            }
 

	
 
            // If here, then we are dealing with a block-like parent block
 
            local_relative_pos = ctx.heap[scope.to_block()].relative_pos_in_parent;
 
        }
 

	
 
        // No collisions at all
 
        let block = &mut ctx.heap[self.cur_scope.as_ref().unwrap().to_block()];
 
        block.locals.push(id);
 

	
 
        Ok(())
 
    }
 

	
 
    /// Finds a variable in the visitor's scope that must appear before the
 
    /// specified relative position within that block.
 
    fn find_variable(&self, ctx: &Ctx, mut relative_pos: u32, identifier: &NamespacedIdentifier2) -> Result<VariableId, ParseError2> {
 
    fn find_variable(&self, ctx: &Ctx, mut relative_pos: u32, identifier: &NamespacedIdentifier) -> Result<VariableId, ParseError2> {
 
        debug_assert!(self.cur_scope.is_some());
 
        debug_assert!(identifier.parts.len() == 1, "implement namespaced seeking of target associated with identifier");
 

	
 
        // TODO: May still refer to an alias of a global symbol using a single
 
        //  identifier in the namespace.
 
        // No need to use iterator over namespaces if here
 
        let mut scope = self.cur_scope.as_ref().unwrap();
 
        
 
        loop {
 
            debug_assert!(scope.is_block());
 
            let block = &ctx.heap[scope.to_block()];
 
            
 
            for local_id in &block.locals {
 
                let local = &ctx.heap[*local_id];
 
                
 
                if local.relative_pos_in_block < relative_pos && identifier.matches_identifier(&local.identifier) {
 
                    return Ok(local_id.upcast());
 
                }
 
            }
 

	
 
            debug_assert!(block.parent_scope.is_some());
 
            scope = block.parent_scope.as_ref().unwrap();
 
            if !scope.is_block() {
 
                // Definition scope, need to check arguments to definition
 
                match scope {
 
                    Scope::Definition(definition_id) => {
 
                        let definition = &ctx.heap[*definition_id];
 
                        for parameter_id in definition.parameters() {
 
                            let parameter = &ctx.heap[*parameter_id];
 
                            if identifier.matches_identifier(&parameter.identifier) {
 
                                return Ok(parameter_id.upcast());
 
                            }
 
                        }
 
                    },
 
                    _ => unreachable!(),
 
                }
 

	
 
                // Variable could not be found
 
                return Err(ParseError2::new_error(
 
                    &ctx.module.source, identifier.position, "This variable is not declared"
 
                ));
 
            } else {
 
                relative_pos = block.relative_pos_in_parent;
 
            }
 
        }
 
    }
 

	
 
    /// Adds a particular label to the current scope. Will return an error if
 
    /// there is another label with the same name visible in the current scope.
 
    fn checked_label_add(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> Result<(), ParseError2> {
 
        debug_assert!(self.cur_scope.is_some());
 

	
 
        // Make sure label is not defined within the current scope or any of the
 
        // parent scope.
 
        let label = &ctx.heap[id];
 
        let mut scope = self.cur_scope.as_ref().unwrap();
 

	
 
        loop {
 
            debug_assert!(scope.is_block(), "scope is not a block");
 
            let block = &ctx.heap[scope.to_block()];
 
            for other_label_id in &block.labels {
 
                let other_label = &ctx.heap[*other_label_id];
 
                if other_label.label == label.label {
 
                    // Collision
 
                    return Err(
 
                        ParseError2::new_error(&ctx.module.source, label.position, "Label name conflicts with another label")
 
                            .with_postfixed_info(&ctx.module.source, other_label.position, "Other label is found here")
 
                    );
 
                }
 
            }
 

	
 
            debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent");
 
            scope = block.parent_scope.as_ref().unwrap();
 
            if !scope.is_block() {
 
                break;
 
            }
 
        }
 

	
 
        // No collisions
 
        let block = &mut ctx.heap[self.cur_scope.as_ref().unwrap().to_block()];
 
        block.labels.push(id);
 

	
 
        Ok(())
 
    }
 

	
 
    /// Finds a particular labeled statement by its identifier. Once found it
 
    /// will make sure that the target label does not skip over any variable
 
    /// declarations within the scope in which the label was found.
 
    fn find_label(&self, ctx: &Ctx, identifier: &Identifier) -> Result<LabeledStatementId, ParseError2> {
 
        debug_assert!(self.cur_scope.is_some());
 

	
 
        let mut scope = self.cur_scope.as_ref().unwrap();
 
        loop {
 
            debug_assert!(scope.is_block(), "scope is not a block");
 
            let relative_scope_pos = ctx.heap[scope.to_block()].relative_pos_in_parent;
 

	
 
            let block = &ctx.heap[scope.to_block()];
 
            for label_id in &block.labels {
 
                let label = &ctx.heap[*label_id];
 
                if label.label == *identifier {
 
                    for local_id in &block.locals {
 
                        // TODO: Better to do this in control flow analysis, it
 
                        //  is legal to skip over a variable declaration if it
 
                        //  is not actually being used. I might be missing
 
                        //  something here when laying out the bytecode...
 
                        let local = &ctx.heap[*local_id];
 
                        if local.relative_pos_in_block > relative_scope_pos && local.relative_pos_in_block < label.relative_pos_in_block {
 
                            return Err(
 
                                ParseError2::new_error(&ctx.module.source, identifier.position, "This target label skips over a variable declaration")
 
                                    .with_postfixed_info(&ctx.module.source, label.position, "Because it jumps to this label")
 
                                    .with_postfixed_info(&ctx.module.source, local.position, "Which skips over this variable")
 
                            );
 
                        }
 
                    }
 
                    return Ok(*label_id);
 
                }
 
            }
 

	
 
            debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent");
 
            scope = block.parent_scope.as_ref().unwrap();
 
            if !scope.is_block() {
 
                return Err(ParseError2::new_error(&ctx.module.source, identifier.position, "Could not find this label"));
 
            }
 

	
 
        }
 
    }
 

	
 
    /// Finds a particular symbol in the symbol table which must correspond to
 
    /// a definition of a particular type.
 
    // Note: root_id, symbols and types passed in explicitly to prevent
 
    //  borrowing errors
 
    fn find_symbol_of_type<'a>(
 
        &self, source: &InputSource, root_id: RootId, symbols: &SymbolTable, types: &'a TypeTable,
 
        identifier: &NamespacedIdentifier2, expected_type_class: TypeClass
 
        identifier: &NamespacedIdentifier, expected_type_class: TypeClass
 
    ) -> Result<&'a DefinedType, ParseError2> {
 
        // Find symbol associated with identifier
 
        let (find_result, _) = find_type_definition(symbols, types, root_id, identifier)
 
            .as_parse_error(source)?;
 

	
 
        let definition_type_class = find_result.definition.type_class();
 
        if expected_type_class != definition_type_class {
 
            return Err(ParseError2::new_error(
 
                source, identifier.position,
 
                &format!(
 
                    "Expected to find a {}, this symbol points to a {}",
 
                    expected_type_class, definition_type_class
 
                )
 
            ))
 
        }
 

	
 
        Ok(find_result)
 
    }
 

	
 
    /// This function will check if the provided while statement ID has a block
 
    /// statement that is one of our current parents.
 
    fn has_parent_while_scope(&self, ctx: &Ctx, id: WhileStatementId) -> bool {
 
        debug_assert!(self.cur_scope.is_some());
 
        let mut scope = self.cur_scope.as_ref().unwrap();
 
        let while_stmt = &ctx.heap[id];
 
        loop {
 
            debug_assert!(scope.is_block());
 
            let block = scope.to_block();
 
            if while_stmt.body == block.upcast() {
 
                return true;
 
            }
 

	
 
            let block = &ctx.heap[block];
 
            debug_assert!(block.parent_scope.is_some(), "block scope does not have a parent");
 
            scope = block.parent_scope.as_ref().unwrap();
 
            if !scope.is_block() {
 
                return false;
 
            }
 
        }
 
    }
 

	
 
    /// This function should be called while dealing with break/continue
 
    /// statements. It will try to find the targeted while statement, using the
 
    /// target label if provided. If a valid target is found then the loop's
 
    /// ID will be returned, otherwise a parsing error is constructed.
 
    /// The provided input position should be the position of the break/continue
 
    /// statement.
 
    fn resolve_break_or_continue_target(&self, ctx: &Ctx, position: InputPosition, label: &Option<Identifier>) -> Result<WhileStatementId, ParseError2> {
 
        let target = match label {
 
            Some(label) => {
 
                let target_id = self.find_label(ctx, label)?;
 

	
 
                // Make sure break target is a while statement
 
                let target = &ctx.heap[target_id];
 
                if let Statement::While(target_stmt) = &ctx.heap[target.body] {
 
                    // Even though we have a target while statement, the break might not be
 
                    // present underneath this particular labeled while statement
 
                    if !self.has_parent_while_scope(ctx, target_stmt.this) {
 
                        ParseError2::new_error(&ctx.module.source, label.position, "Break statement is not nested under the target label's while statement")
 
                            .with_postfixed_info(&ctx.module.source, target.position, "The targeted label is found here");
 
                    }
 

	
 
                    target_stmt.this
 
                } else {
 
                    return Err(
 
                        ParseError2::new_error(&ctx.module.source, label.position, "Incorrect break target label, it must target a while loop")
 
                            .with_postfixed_info(&ctx.module.source, target.position, "The targeted label is found here")
 
                    );
 
                }
 
            },
 
            None => {
 
                // Use the enclosing while statement, the break must be
 
                // nested within that while statement
 
                if self.in_while.is_none() {
 
                    return Err(
 
                        ParseError2::new_error(&ctx.module.source, position, "Break statement is not nested under a while loop")
 
                    );
 
                }
 

	
 
                self.in_while.unwrap()
 
            }
 
        };
 

	
 
        // We have a valid target for the break statement. But we need to
 
        // make sure we will not break out of a synchronous block
 
        {
 
            let target_while = &ctx.heap[target];
 
            if target_while.in_sync != self.in_sync {
 
                // Break is nested under while statement, so can only escape a
 
                // sync block if the sync is nested inside the while statement.
 
                debug_assert!(self.in_sync.is_some());
 
                let sync_stmt = &ctx.heap[self.in_sync.unwrap()];
 
                return Err(
 
                    ParseError2::new_error(&ctx.module.source, position, "Break may not escape the surrounding synchronous block")
 
                        .with_postfixed_info(&ctx.module.source, target_while.position, "The break escapes out of this loop")
 
                        .with_postfixed_info(&ctx.module.source, sync_stmt.position, "And would therefore escape this synchronous block")
0 comments (0 inline, 0 general)