dolang-compile 0.1.0

Parser, resolver, and bytecode compiler for the Do language.
Documentation
use crate::constant;

use super::sym;

use dolang_bytecode::Variadic;
use dolang_util::intern;

pub(crate) struct PackTag;

pub(crate) type PackId = intern::Id<PackTag>;

#[cfg(feature = "debug")]
use super::Compiler;

#[cfg(feature = "debug")]
use std::fmt::{self, Write};

#[derive(Hash, PartialEq, Eq, Debug, Clone)]
pub(crate) enum Arg {
    Value,
    Pack,
    Key(sym::Id),
}

#[derive(Hash, PartialEq, Eq, Debug, Clone)]
pub(crate) struct Pack(Vec<Arg>);

impl Pack {
    pub(crate) fn new(iter: impl Iterator<Item = Arg>) -> Self {
        Self(iter.collect())
    }

    pub(crate) fn iter(&self) -> impl Iterator<Item = &Arg> {
        self.0.iter()
    }

    #[expect(dead_code)]
    pub(crate) fn into_iter(self) -> impl Iterator<Item = Arg> {
        self.0.into_iter()
    }
}

#[cfg(feature = "debug")]
impl Pack {
    pub fn dump(&self, compiler: &Compiler, w: &mut impl Write) -> fmt::Result {
        let mut pos = 0;
        let mut need_space = false;
        for p in self.0.iter() {
            match p {
                Arg::Value => pos += 1,
                p => {
                    if pos != 0 {
                        if need_space {
                            write!(w, " ")?;
                        }
                        write!(w, "{}", pos)?;
                        need_space = true;
                        pos = 0;
                    }
                    if need_space {
                        write!(w, " ")?;
                    }
                    match p {
                        Arg::Value => unreachable!(),
                        Arg::Pack => write!(w, "")?,
                        Arg::Key(id) => write!(w, "{}", &compiler.bintab[compiler.symtab[*id]])?,
                    }
                    need_space = true;
                }
            }
        }
        if pos != 0 {
            if need_space {
                write!(w, " ")?;
            }
            write!(w, "{}", pos)?;
        }
        Ok(())
    }
}

pub(crate) type PackTable = intern::Table<Pack, PackTag>;

pub(crate) struct UnpackTag;

pub(crate) type UnpackId = intern::Id<UnpackTag>;

#[derive(Hash, PartialEq, Eq, Debug, Clone, PartialOrd, Ord)]
pub(crate) enum UnpackKeyKind {
    Sym(sym::Id),
    Const(constant::Id),
}

#[derive(Hash, PartialEq, Eq, Debug, Clone, PartialOrd, Ord)]
pub(crate) struct UnpackKey {
    pub(crate) kind: UnpackKeyKind,
    pub(crate) default: Option<constant::Id>,
}

#[derive(Hash, PartialEq, Eq, Debug, Clone)]
pub(crate) struct Unpack {
    required: usize,
    optional: Vec<constant::Id>,
    keys: Vec<UnpackKey>,
    variadic: Variadic,
}

impl Unpack {
    pub(crate) fn new(
        required: usize,
        optional: impl IntoIterator<Item = constant::Id>,
        keys: impl IntoIterator<Item = UnpackKey>,
        variadic: Variadic,
    ) -> Self {
        let mut keys: Vec<_> = keys.into_iter().collect();
        keys.sort();
        Self {
            required,
            optional: optional.into_iter().collect(),
            keys,
            variadic,
        }
    }

    pub(crate) fn required(&self) -> usize {
        self.required
    }

    pub(crate) fn optional(&self) -> impl DoubleEndedIterator<Item = &constant::Id> {
        self.optional.iter()
    }

    pub(crate) fn variadic(&self) -> Variadic {
        self.variadic
    }

    pub(crate) fn iter_keys(&self) -> impl DoubleEndedIterator<Item = &UnpackKey> {
        self.keys.iter()
    }

    pub(crate) fn len(&self) -> usize {
        self.required
            .strict_add(self.optional.len())
            .strict_add(self.keys.len())
            .strict_add(match self.variadic {
                Variadic::None | Variadic::Discard => 0,
                Variadic::Capture => 1,
            })
    }
}

#[cfg(feature = "debug")]
impl Unpack {
    pub fn dump(&self, compiler: &Compiler, w: &mut impl Write) -> fmt::Result {
        let mut need_space = false;
        if self.required != 0 {
            write!(w, "{}", self.required)?;
            need_space = true;
        }
        if !self.optional.is_empty() {
            if need_space {
                write!(w, " ")?;
            }
            write!(w, "(")?;
            for id in self.optional.iter() {
                compiler.consttab[*id].dump(compiler, w)?
            }
            write!(w, ")")?;
            need_space = true;
        }
        for id in self.keys.iter() {
            if need_space {
                write!(w, " ")?;
            }
            match &id.kind {
                UnpackKeyKind::Sym(sym) => {
                    write!(w, "{}", &compiler.bintab[compiler.symtab[*sym]])?;
                }
                UnpackKeyKind::Const(c) => {
                    compiler.consttab[*c].dump(compiler, w)?;
                }
            }
            if let Some(default) = &id.default {
                write!(w, "(")?;
                compiler.consttab[*default].dump(compiler, w)?;
                write!(w, ")")?;
            }
            need_space = true;
        }
        if self.variadic != Variadic::None {
            if need_space {
                write!(w, " ")?;
            }
            write!(w, "")?;
        }
        Ok(())
    }
}

pub(crate) type UnpackTable = intern::Table<Unpack, UnpackTag>;