139 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
//! The program node of the syntax tree.
 | 
						|
 | 
						|
use getset::Getters;
 | 
						|
 | 
						|
use crate::{
 | 
						|
    base::{
 | 
						|
        source_file::{SourceElement, Span},
 | 
						|
        Handler,
 | 
						|
    },
 | 
						|
    lexical::token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
 | 
						|
    syntax::{
 | 
						|
        error::{Error, SyntaxKind, UnexpectedSyntax},
 | 
						|
        parser::{Parser, Reading},
 | 
						|
    },
 | 
						|
};
 | 
						|
 | 
						|
use super::declaration::Declaration;
 | 
						|
 | 
						|
/// Program is a collection of declarations preceeded by a namespace selector.
 | 
						|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
						|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
 | 
						|
pub struct ProgramFile {
 | 
						|
    /// The namespace selector.
 | 
						|
    #[get = "pub"]
 | 
						|
    namespace: Namespace,
 | 
						|
    /// The declarations within the program.
 | 
						|
    #[get = "pub"]
 | 
						|
    declarations: Vec<Declaration>,
 | 
						|
}
 | 
						|
 | 
						|
/// Namespace is a namespace selector.
 | 
						|
///
 | 
						|
/// Syntax Synopsis:
 | 
						|
///
 | 
						|
/// ```ebnf
 | 
						|
/// Namespace:
 | 
						|
///    'namespace' StringLiteral ';' ;
 | 
						|
/// ```
 | 
						|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
						|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
 | 
						|
#[allow(missing_docs)]
 | 
						|
pub struct Namespace {
 | 
						|
    /// The `namespace` keyword.
 | 
						|
    #[get = "pub"]
 | 
						|
    namespace_keyword: Keyword,
 | 
						|
    /// The name of the namespace.
 | 
						|
    #[get = "pub"]
 | 
						|
    namespace_name: StringLiteral,
 | 
						|
    /// The semicolon.
 | 
						|
    #[get = "pub"]
 | 
						|
    semicolon: Punctuation,
 | 
						|
}
 | 
						|
 | 
						|
impl SourceElement for Namespace {
 | 
						|
    fn span(&self) -> Span {
 | 
						|
        self.namespace_keyword
 | 
						|
            .span()
 | 
						|
            .join(&self.semicolon.span())
 | 
						|
            .expect("Invalid span")
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl Namespace {
 | 
						|
    /// Dissolves the namespace into its components.
 | 
						|
    #[must_use]
 | 
						|
    pub fn dissolve(self) -> (Keyword, StringLiteral, Punctuation) {
 | 
						|
        (self.namespace_keyword, self.namespace_name, self.semicolon)
 | 
						|
    }
 | 
						|
 | 
						|
    /// Validates a namespace string.
 | 
						|
    #[must_use]
 | 
						|
    pub fn validate_str(namespace: &str) -> bool {
 | 
						|
        const VALID_CHARS: &str = "0123456789abcdefghijklmnopqrstuvwxyz_-.";
 | 
						|
 | 
						|
        namespace.chars().all(|c| VALID_CHARS.contains(c))
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl<'a> Parser<'a> {
 | 
						|
    /// Parses a [`ProgramFile`].
 | 
						|
    pub fn parse_program(&mut self, handler: &impl Handler<Error>) -> Option<ProgramFile> {
 | 
						|
        let namespace = match self.stop_at_significant() {
 | 
						|
            Reading::Atomic(Token::Keyword(namespace_keyword))
 | 
						|
                if namespace_keyword.keyword == KeywordKind::Namespace =>
 | 
						|
            {
 | 
						|
                // eat the keyword
 | 
						|
                self.forward();
 | 
						|
 | 
						|
                let namespace_name = self
 | 
						|
                    .parse_string_literal(handler)
 | 
						|
                    .and_then(|name| Namespace::validate_str(name.str_content()).then_some(name))?;
 | 
						|
 | 
						|
                let semicolon = self.parse_punctuation(';', true, handler)?;
 | 
						|
 | 
						|
                Some(Namespace {
 | 
						|
                    namespace_keyword,
 | 
						|
                    namespace_name,
 | 
						|
                    semicolon,
 | 
						|
                })
 | 
						|
            }
 | 
						|
            unexpected => {
 | 
						|
                handler.receive(
 | 
						|
                    UnexpectedSyntax {
 | 
						|
                        expected: SyntaxKind::Keyword(KeywordKind::Namespace),
 | 
						|
                        found: unexpected.into_token(),
 | 
						|
                    }
 | 
						|
                    .into(),
 | 
						|
                );
 | 
						|
                None
 | 
						|
            }
 | 
						|
        }?;
 | 
						|
 | 
						|
        let mut declarations = Vec::new();
 | 
						|
 | 
						|
        while !self.is_exhausted() {
 | 
						|
            let result = self.parse_declaration(handler);
 | 
						|
 | 
						|
            #[allow(clippy::option_if_let_else)]
 | 
						|
            if let Some(x) = result {
 | 
						|
                declarations.push(x);
 | 
						|
            } else {
 | 
						|
                self.stop_at(|reading| {
 | 
						|
                    matches!(
 | 
						|
                        reading,
 | 
						|
                        Reading::IntoDelimited(x) if x.punctuation == '{'
 | 
						|
                    )
 | 
						|
                });
 | 
						|
 | 
						|
                self.next_token();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        Some(ProgramFile {
 | 
						|
            namespace,
 | 
						|
            declarations,
 | 
						|
        })
 | 
						|
    }
 | 
						|
}
 |