~jojo/effem

a42f603bcdfeaae93866e160c8b8f4cd9dff26f0 — JoJo 1 year, 20 days ago 6598d9f
pretty print Base IR
6 files changed, 370 insertions(+), 25 deletions(-)

M src/base.rs
M src/cache.rs
M src/diag.rs
M src/eval.rs
M src/main.rs
M src/resolve.rs
M src/base.rs => src/base.rs +231 -0
@@ 1,5 1,6 @@
use crate::prelude::*;
use std::collections::HashSet;
use std::fmt::{Display, Formatter, Result as FResult, Write as FWrite};

#[derive(Clone, Debug)]
pub enum GlobDef {


@@ 117,6 118,16 @@ pub enum Const {
    F64(f64),
}

impl Body {
    pub fn get_stm(&self, StmRef(i): StmRef) -> &Stm {
        &self.stm_arena[i as usize]
    }

    pub fn get_block(&self, BlockRef(i): BlockRef) -> &Block {
        &self.block_arena[i as usize]
    }
}

macro_rules! impl_const_from {
    ($t:ty, $c:expr) => {
        impl From<$t> for Const {


@@ 169,3 180,223 @@ impl_const_arithm_binop!(std::ops::Sub, sub);
impl_const_arithm_binop!(std::ops::Mul, mul);
impl_const_arithm_binop!(std::ops::Div, div);
impl_const_arithm_binop!(std::ops::Rem, rem);

impl Display for BlockRef {
    fn fmt(&self, f: &mut Formatter) -> FResult {
        write!(f, "b{}", self.0)
    }
}

impl Display for StmRef {
    fn fmt(&self, f: &mut Formatter) -> FResult {
        write!(f, "s{}", self.0)
    }
}

impl Display for ParamRef {
    fn fmt(&self, f: &mut Formatter) -> FResult {
        write!(f, "p{}", self.0)
    }
}

impl Display for Binop {
    fn fmt(&self, f: &mut Formatter) -> FResult {
        use Binop::*;
        write!(
            f,
            "{}",
            match *self {
                Add => "+",
                Sub => "-",
                Mul => "*",
                Quot => "quot",
                Rem => "rem",
                Lt => "<",
                Gt => ">",
                Leq => "<=",
                Geq => ">=",
                Eq => "=",
                Neq => "/=",
            }
        )
    }
}

impl Display for Operand {
    fn fmt(&self, f: &mut Formatter) -> FResult {
        match *self {
            Operand::Const(x) => write!(f, "{}", x),
            Operand::Var(x) => write!(f, "{}", x),
            Operand::Param(x) => write!(f, "{}", x),
            Operand::Global(x) => write!(f, "g{}", x),
        }
    }
}

impl Display for Const {
    fn fmt(&self, f: &mut Formatter) -> FResult {
        use Const::*;
        match *self {
            Bool(x) => write!(f, "{}", x),
            I8(x) => write!(f, "{}i8", x),
            I16(x) => write!(f, "{}i16", x),
            I32(x) => write!(f, "{}i32", x),
            I64(x) => write!(f, "{}i64", x),
            Int(x) => write!(f, "{}i", x),
            N8(x) => write!(f, "{}n8", x),
            N16(x) => write!(f, "{}n16", x),
            N32(x) => write!(f, "{}n32", x),
            N64(x) => write!(f, "{}n64", x),
            Nat(x) => write!(f, "{}n", x),
            F64(x) => write!(f, "{}f64", x),
        }
    }
}

impl Pretty for (DefId, &GlobDef) {
    type LocalCtx = ();
    fn pretty(&self, pr: &mut Prettier<()>) -> FResult {
        writeln!(pr, ";; {}", pr.cache.get_def_name(self.0))?;
        match self.1 {
            GlobDef::FunDef(d) => (self.0, d).pretty(pr),
            GlobDef::VarDef(d) => (self.0, d).pretty(pr),
        }
    }
}

impl Pretty for (DefId, &FunDef) {
    type LocalCtx = ();
    fn pretty(&self, pr: &mut Prettier<()>) -> FResult {
        let id = self.0;
        let def = self.1;
        write!(pr, "(defun g{id} [")?;
        pr.write_sep(def.params.iter().map(|_| "_"), " ")?;
        pr.write_str("]")?;
        pr.newline_indent()?;
        pr.add_indent(2, |pr| {
            write!(pr, ":children [")?;
            pr.write_sep(&self.1.children, " ")?;
            write!(pr, "]")?;
            pr.newline_indent()?;
            self.1.body.pretty(pr)?;
            pr.write_char(')')
        })
    }
}

impl Pretty for (DefId, &GVarDef) {
    type LocalCtx = ();
    fn pretty(&self, pr: &mut Prettier<()>) -> FResult {
        let id = self.0;
        let def = self.1;
        write!(pr, "(define g{id}")?;
        pr.newline_indent()?;
        pr.add_indent(2, |pr| {
            write!(pr, ":children [")?;
            pr.write_sep(&def.children, " ")?;
            write!(pr, "]")?;
            pr.newline_indent()?;
            def.body.pretty(pr)?;
            pr.write_char(')')
        })
    }
}

impl Pretty for Body {
    type LocalCtx = ();
    fn pretty(&self, pr: &mut Prettier<()>) -> FResult {
        write!(pr, ":root {}", self.root)?;
        pr.newline_indent()?;

        pr.with_local_ctx(self, |pr| {
            let mut it = self.block_arena.iter().enumerate();
            if let Some((i, b)) = it.next() {
                (BlockRef(i as u32), b).pretty(pr)?;
                for (i, b) in it {
                    pr.newline_indent()?;
                    (BlockRef(i as u32), b).pretty(pr)?;
                }
            }
            Ok(())
        })
    }
}

impl Pretty for (BlockRef, &Block) {
    type LocalCtx = Body;
    fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
        write!(pr, "(block {}", self.0)?;
        pr.newline_indent()?;
        pr.add_indent(2, |pr| {
            for sref in self.1.stms.iter().cloned() {
                (sref, pr.local_ctx.get_stm(sref)).pretty(pr)?;
                pr.newline_indent()?;
            }
            self.1.tail.pretty(pr)?;
            pr.write_char(')')
        })
    }
}

impl Pretty for (StmRef, &Stm) {
    type LocalCtx = Body;
    fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
        write!(pr, "[{} ", self.0)?;
        self.1.pretty(pr)?;
        pr.write_char(']')
    }
}

impl Pretty for Stm {
    type LocalCtx = Body;
    fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
        match self {
            Stm::Let { rhs } => {
                pr.write_str("(let ")?;
                rhs.pretty(pr)?;
                pr.write_char(')')
            }
        }
    }
}

impl Pretty for Flow {
    type LocalCtx = Body;
    fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
        match self {
            Flow::Produce(ration) => ration.pretty(pr),
            Flow::Diverge(div) => div.pretty(pr),
        }
    }
}

impl Pretty for Operation {
    type LocalCtx = Body;
    fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
        match self {
            Operation::Binop(op, a, b) => write!(pr, "({op} {a} {b})"),
            Operation::Call(f, args) => {
                write!(pr, "(call {f} [")?;
                pr.write_sep(args, " ")?;
                pr.write_str("])")
            }
            Operation::Wrap(rand) => rand.pretty(pr),
        }
    }
}

impl Pretty for Operand {
    type LocalCtx = Body;
    fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
        write!(pr, "{}", self)
    }
}

impl Pretty for Diverge {
    type LocalCtx = Body;
    fn pretty(&self, pr: &mut Prettier<Body>) -> FResult {
        match self {
            Diverge::If(pred, conseq, alt) => write!(pr, "(if {pred} {conseq} {alt})"),
        }
    }
}

M src/cache.rs => src/cache.rs +11 -7
@@ 149,7 149,7 @@ impl Cache {
        } else {
            let module_id =
                *self.parent_modules.get(&def_id).expect("ice: if there's a def id, there should be a parent module");
            let def_ident = self.fetch_def_name(def_id).last().clone();
            let def_ident = self.get_def_name(def_id).last().clone();
            let module = self.fetch_parsed_module(module_id)?;
            let parsed = module[&def_ident].clone();
            let resolved = resolve::resolve_def(self, module_id, &parsed.1)?;


@@ 266,7 266,7 @@ impl Cache {

    fn gen_def_resolution(&mut self, def_ident: String, module_id: ModuleId) -> DefId {
        debug_assert!(self.fetch_parsed_module(module_id).unwrap().contains_key(&def_ident));
        let def_name = self.fetch_module_name(module_id).clone().with(def_ident);
        let def_name = self.get_module_name(module_id).clone().with(def_ident);
        match self.resolved_names.get(&def_name) {
            Some(Res::Def(def_id)) => *def_id,
            Some(res) => panic!("ice: fetching/generating definition resolution for name {def_name}, but that name already resolves to {res:?}"),


@@ 287,11 287,11 @@ impl Cache {
        self.n_defs - 1
    }

    pub fn fetch_def_name(&self, def_id: DefId) -> &FullName<String> {
    pub fn get_def_name(&self, def_id: DefId) -> &FullName<String> {
        self.resolved_names_rev.get(&Res::Def(def_id)).expect("ice: if we have a def id, there should be a reverse")
    }

    pub fn fetch_module_name(&self, module_id: ModuleId) -> &FullName<String> {
    pub fn get_module_name(&self, module_id: ModuleId) -> &FullName<String> {
        self.resolved_names_rev
            .get(&Res::Module(module_id))
            .expect("ice: if we have a module id, there should be a reverse")


@@ 321,20 321,24 @@ impl Cache {

    pub fn fetch_source(&mut self, file_id: FileId) -> Result<&str> {
        if !self.sources.contains_key(&file_id) {
            let path = self.fetch_path(file_id);
            let path = self.get_path(file_id);
            let src = std::fs::read_to_string(path).unwrap();
            self.sources.insert(file_id, src);
        }
        Ok(&self.sources[&file_id])
    }

    pub fn fetch_module_file(&self, module_id: ModuleId) -> FileId {
    pub fn get_module_file(&self, module_id: ModuleId) -> FileId {
        *self.module_files.get(&module_id).expect("ice: we shouldn't have a module id without an associated file id")
    }

    pub fn fetch_path(&self, file_id: FileId) -> &Path {
    pub fn get_path(&self, file_id: FileId) -> &Path {
        self.file_paths.get(&file_id).expect("ice: we shouldn't have a file id without an associated file path")
    }

    pub fn get_abaseds(&self) -> &HashMap<DefId, base::GlobDef> {
        &self.abaseds
    }
}

#[derive(Debug, Clone)]

M src/diag.rs => src/diag.rs +116 -13
@@ 2,7 2,7 @@ use crate::prelude::*;
use fem::Type as FType;
use resolve::Type as RType;
use std::collections::HashSet;
use std::io::*;
use std::fmt::{Debug, Display, Error as FError, Formatter, Result as FResult, Write as FWrite};
use std::path::{Path, PathBuf};

pub type Result<T> = std::result::Result<T, Error>;


@@ 99,7 99,7 @@ impl ResolveErr {
        use ResolveErr::*;
        match self {
            UndefInMod(def, mid) => {
                let mname = cache.fetch_module_name(*mid);
                let mname = cache.get_module_name(*mid);
                def.loc.error(cache, format!("Couldn't find definition `{def}` in module `{mname}`."))
            }
            InvalidRootNamespace(ns) =>


@@ 109,7 109,7 @@ impl ResolveErr {
                .error(cache, format!("Failed to resolve the name `{name}` to any existing module or definition.")),
            ParentIsNotModule(loc, parent) =>
                loc.error(cache, format!("The parent `{}` does not resolve to a module.", parent)),
            DupDef(loc) => loc.error(cache, format!("Duplicate definition of this name in this module.")),
            DupDef(loc) => loc.error(cache, "Duplicate definition of this name in this module."),
        }
    }
}


@@ 123,7 123,6 @@ pub struct ParseErr {

impl ParseErr {
    pub fn render(&self, cache: &mut Cache) -> String {
        use std::fmt::Write;
        self.loc.error(
            cache,
            if self.expecteds.len() == 1 {


@@ 161,8 160,8 @@ impl LexErr {
    }
}

impl std::fmt::Debug for LexErr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
impl Debug for LexErr {
    fn fmt(&self, f: &mut Formatter) -> FResult {
        write!(f, "loc={:?}: expected {}, found {}", self.loc, self.expected, self.found)
    }
}


@@ 175,8 174,8 @@ pub enum LexThing {
    Anon,
}

impl std::fmt::Display for LexThing {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
impl Display for LexThing {
    fn fmt(&self, f: &mut Formatter) -> FResult {
        match self {
            LexThing::Char(c) => write!(f, "char {:?}", c),
            LexThing::Descr(desc) => write!(f, "{}", desc),


@@ 205,19 204,19 @@ impl Loc {
    pub fn with_offset(&self, offset: u32) -> Self {
        use Loc::*;
        match self {
            File { file, .. } | FileGeneral { file } => File { file: file.clone(), offset },
            &File { file, .. } | &FileGeneral { file } => File { file, offset },
            Anon { .. } | AnonGeneral => Anon { offset },
        }
    }

    pub fn error<M: std::fmt::Display>(&self, cache: &mut Cache, msg: M) -> String {
    pub fn error<M: Display>(&self, cache: &mut Cache, msg: M) -> String {
        self.contextualize(cache, format!("error: {msg}"))
    }

    pub fn contextualize<M: std::fmt::Display>(&self, cache: &mut Cache, msg: M) -> String {
    pub fn contextualize<M: Display>(&self, cache: &mut Cache, msg: M) -> String {
        match self {
            Loc::File { file, offset } => {
                let path = cache.fetch_path(*file).to_owned();
                let path = cache.get_path(*file).to_owned();
                let src = cache.fetch_source(*file).unwrap();
                let (line_i, column_i, line_s) = line_and_column_at(src, *offset);
                let gutter = " ".repeat(column_i.max(1).ilog10() as usize + 1);


@@ 226,7 225,7 @@ impl Loc {
                format!("{path}:{line_i}:{column_i}: {msg}\n{gutter} |\n{column_i} | {line_s}\n{gutter} | {marker}^",)
            }
            Loc::FileGeneral { file } => {
                let path = cache.fetch_path(*file);
                let path = cache.get_path(*file);
                format!("{}: {msg}", path.to_string_lossy())
            }
            Loc::Anon { offset } => {


@@ 260,3 259,107 @@ fn line_and_column_at(mut s: &str, mut offset: u32) -> (u32, u32, &str) {
        offset -= i as u32 + 1;
    }
}

pub struct DisplayPretty<'ca, 'ct, T, LocalCtx> {
    pub this: T,
    pub cache: &'ca Cache,
    pub column: usize,
    pub local_ctx: &'ct LocalCtx,
}

impl<'ca, 'ct, P, LocalCtx> Display for DisplayPretty<'ca, 'ct, P, LocalCtx>
where
    P: Pretty<LocalCtx = LocalCtx>,
{
    fn fmt(&self, f: &mut Formatter) -> FResult {
        self.this.pretty(&mut Prettier { out: f, cache: self.cache, column0: self.column, local_ctx: self.local_ctx })
    }
}

pub trait Pretty {
    type LocalCtx;
    fn pretty(&self, st: &mut Prettier<Self::LocalCtx>) -> FResult;
}

impl<P: Pretty> Pretty for &P {
    type LocalCtx = P::LocalCtx;
    fn pretty(&self, st: &mut Prettier<P::LocalCtx>) -> FResult {
        (**self).pretty(st)
    }
}

pub struct Prettier<'w, 'ca, 'ct, LocalCtx> {
    pub out: &'w mut dyn FWrite,
    pub cache: &'ca Cache,
    column0: usize,
    pub local_ctx: &'ct LocalCtx,
}

impl<'w, 'ca, 'ct, LocalCtx> Prettier<'w, 'ca, 'ct, LocalCtx> {
    pub fn with_local_ctx<'ct2, LocalCtx2, A>(
        &mut self,
        ctx: &'ct2 LocalCtx2,
        f: impl FnOnce(&mut Prettier<LocalCtx2>) -> std::result::Result<A, FError>,
    ) -> std::result::Result<A, FError> {
        f(&mut Prettier { out: self.out, cache: self.cache, column0: self.column0, local_ctx: ctx })
    }

    pub fn add_indent<A>(
        &mut self,
        columns: usize,
        f: impl FnOnce(&mut Prettier<LocalCtx>) -> std::result::Result<A, FError>,
    ) -> std::result::Result<A, FError> {
        for _ in 0..columns {
            self.write_char(' ')?;
        }
        self.at_added_indent(columns, f)
    }

    pub fn at_added_indent<A>(
        &mut self,
        columns: usize,
        f: impl FnOnce(&mut Prettier<LocalCtx>) -> std::result::Result<A, FError>,
    ) -> std::result::Result<A, FError> {
        self.column0 += columns;
        let y = f(self)?;
        self.column0 -= columns;
        Ok(y)
    }

    pub fn indent(&mut self) -> FResult {
        for _ in 0..self.column0 {
            self.write_char(' ')?;
        }
        Ok(())
    }

    pub fn newline_indent(&mut self) -> FResult {
        self.write_char('\n')?;
        self.indent()
    }

    pub fn write_sep(&mut self, xs: impl IntoIterator<Item = impl Display>, sep: &str) -> FResult {
        let mut xs = xs.into_iter();
        if let Some(x) = xs.next() {
            write!(self, "{x}")?;
            for x in xs {
                write!(self, "{sep}{x}")?;
            }
        }
        Ok(())
    }
}

impl<'w, 'ca, 'ct, LocalCtx> FWrite for Prettier<'w, 'ca, 'ct, LocalCtx> {
    fn write_str(&mut self, s: &str) -> FResult {
        self.out.write_str(s)
    }
}

/// Estimate how wide the gived string rendered as glyphs would be in monospace columns.
/// This will never be 100% accurate for all systems, as each font can display a "character" however widely
/// it wishes, and we don't limit users to e.g. the subset of latin characters & symbols.
pub fn estim_display_width(s: &str) -> usize {
    // TODO: Use library which looks at grapheme clusters & stuff to get more accurate estimation
    s.chars().count()
}

M src/eval.rs => src/eval.rs +4 -4
@@ 28,8 28,8 @@ impl<'c, 'd, 'a> EvalDef<'c, 'd, 'a> {
        self.eval_block_ref(self.body.root)
    }

    fn eval_block_ref(&mut self, BlockRef(i): BlockRef) -> Result<Operand> {
        self.eval_block(&self.body.block_arena[i as usize])
    fn eval_block_ref(&mut self, bref: BlockRef) -> Result<Operand> {
        self.eval_block(&self.body.get_block(bref))
    }

    fn eval_block(&mut self, block: &Block) -> Result<Operand> {


@@ 44,8 44,8 @@ impl<'c, 'd, 'a> EvalDef<'c, 'd, 'a> {
        Ok(())
    }

    fn eval_stm_ref(&mut self, lhs @ StmRef(i): StmRef) -> Result<()> {
        let stm = &self.body.stm_arena[i as usize];
    fn eval_stm_ref(&mut self, lhs: StmRef) -> Result<()> {
        let stm = &self.body.get_stm(lhs);
        match stm {
            Stm::Let { rhs } => {
                let val = self.eval_div(rhs)?;

M src/main.rs => src/main.rs +7 -0
@@ 34,6 34,13 @@ fn main() {
    if let Err(msg) = with_pretty_errors(&mut cache, |cache| {
        let id = cache.fetch_global_resolved_name(&main_name)?.as_def().unwrap();
        println!("result: {:?}", cache.fetch_evaluated(id)?);
        let mut f = std::fs::File::create(".dbg.base").unwrap();
        use std::io::Write;
        let mut abaseds = cache.get_abaseds().iter().collect::<Vec<_>>();
        abaseds.sort_by_key(|(k, _)| *k);
        for def in abaseds.into_iter().map(|(k, v)| (*k, v)) {
            writeln!(f, "{}\n", DisplayPretty { this: def, cache, column: 0, local_ctx: &() }).unwrap()
        }
        Ok(())
    }) {
        eprintln!("{msg}")

M src/resolve.rs => src/resolve.rs +1 -1
@@ 44,7 44,7 @@ struct Resolver<'c> {
impl<'c> Resolver<'c> {
    fn new(cache: &'c mut Cache, module: ModuleId) -> Result<Self> {
        let module_top_level = cache.fetch_module_local_resolved_names(module)?.clone();
        let file = cache.fetch_module_file(module);
        let file = cache.get_module_file(module);
        Ok(Self { cache, local_count: 0, scopes: vec![module_top_level], module, file })
    }