shuck-semantic 0.0.16

Semantic analysis model for shell scripts with scopes, bindings, and dataflow
Documentation
use bitflags::bitflags;
use shuck_ast::{Name, Span};

use crate::{DeclarationBuiltin, ReferenceId, ScopeId};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BindingId(pub(crate) u32);

impl BindingId {
    pub(crate) fn index(self) -> usize {
        self.0 as usize
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Binding {
    pub id: BindingId,
    pub name: Name,
    pub kind: BindingKind,
    pub origin: BindingOrigin,
    pub scope: ScopeId,
    pub span: Span,
    pub references: Vec<ReferenceId>,
    pub attributes: BindingAttributes,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BindingOrigin {
    Assignment {
        definition_span: Span,
        value: AssignmentValueOrigin,
    },
    LoopVariable {
        definition_span: Span,
        items: LoopValueOrigin,
    },
    ParameterDefaultAssignment {
        definition_span: Span,
    },
    Imported {
        definition_span: Span,
    },
    FunctionDefinition {
        definition_span: Span,
    },
    BuiltinTarget {
        definition_span: Span,
        kind: BuiltinBindingTargetKind,
    },
    ArithmeticAssignment {
        definition_span: Span,
        target_span: Span,
    },
    Declaration {
        definition_span: Span,
    },
    Nameref {
        definition_span: Span,
    },
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AssignmentValueOrigin {
    PlainScalarAccess,
    StaticLiteral,
    ParameterOperator,
    Transformation,
    IndirectExpansion,
    CommandOrProcessSubstitution,
    MixedDynamic,
    ArrayOrCompound,
    Unknown,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoopValueOrigin {
    StaticWords,
    ExpandedWords,
    ImplicitArgv,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuiltinBindingTargetKind {
    Read,
    Mapfile,
    Printf,
    Getopts,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BindingKind {
    Assignment,
    ParameterDefaultAssignment,
    AppendAssignment,
    ArrayAssignment,
    Declaration(DeclarationBuiltin),
    FunctionDefinition,
    LoopVariable,
    ReadTarget,
    MapfileTarget,
    PrintfTarget,
    GetoptsTarget,
    ArithmeticAssignment,
    Nameref,
    Imported,
}

pub(crate) fn is_array_like_binding(binding: &Binding) -> bool {
    binding
        .attributes
        .intersects(BindingAttributes::ARRAY | BindingAttributes::ASSOC)
        || matches!(binding.kind, BindingKind::ArrayAssignment)
}

bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    pub struct BindingAttributes: u16 {
        const EXPORTED               = 0b0000_0001;
        const READONLY               = 0b0000_0010;
        const LOCAL                  = 0b0000_0100;
        const INTEGER                = 0b0000_1000;
        const ARRAY                  = 0b0001_0000;
        const ASSOC                  = 0b0010_0000;
        const NAMEREF                = 0b0100_0000;
        const LOWERCASE              = 0b1000_0000;
        const UPPERCASE              = 0b0001_0000_0000;
        const DECLARATION_INITIALIZED = 0b0010_0000_0000;
        const IMPORTED_POSSIBLE      = 0b0100_0000_0000;
        const IMPORTED_FUNCTION      = 0b1000_0000_0000;
        const EMPTY_INITIALIZER      = 0b0001_0000_0000_0000;
        const IMPORTED_FILE_ENTRY    = 0b0010_0000_0000_0000;
        const SELF_REFERENTIAL_READ  = 0b0100_0000_0000_0000;
    }
}