~jojo/effem

1869251490bc90915dc84706fef6810af1fd4b7e — JoJo 3 months ago a42f603 main
Effem is now Kapreolo and lives in another repo
19 files changed, 2 insertions(+), 3711 deletions(-)

D .gitignore
D Cargo.lock
D Cargo.toml
D LICENSE
M README.md
D src/abase.rs
D src/base.rs
D src/cache.rs
D src/check.rs
D src/desugar.rs
D src/diag.rs
D src/eval.rs
D src/fem.rs
D src/lex.rs
D src/main.rs
D src/name.rs
D src/parse.rs
D src/prelude.rs
D src/resolve.rs
D .gitignore => .gitignore +0 -1
@@ 1,1 0,0 @@
/target

D Cargo.lock => Cargo.lock +0 -264
@@ 1,264 0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"

[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "effem"
version = "0.1.0"
dependencies = [
 "tempfile",
]

[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
 "errno-dragonfly",
 "libc",
 "windows-sys 0.48.0",
]

[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
 "cc",
 "libc",
]

[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
 "instant",
]

[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"

[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
 "cfg-if",
]

[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
 "hermit-abi",
 "libc",
 "windows-sys 0.48.0",
]

[[package]]
name = "libc"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"

[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"

[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
 "bitflags",
]

[[package]]
name = "rustix"
version = "0.37.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
dependencies = [
 "bitflags",
 "errno",
 "io-lifetimes",
 "libc",
 "linux-raw-sys",
 "windows-sys 0.48.0",
]

[[package]]
name = "tempfile"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
dependencies = [
 "cfg-if",
 "fastrand",
 "redox_syscall",
 "rustix",
 "windows-sys 0.45.0",
]

[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
 "windows-targets 0.42.2",
]

[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
 "windows-targets 0.48.0",
]

[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
 "windows_aarch64_gnullvm 0.42.2",
 "windows_aarch64_msvc 0.42.2",
 "windows_i686_gnu 0.42.2",
 "windows_i686_msvc 0.42.2",
 "windows_x86_64_gnu 0.42.2",
 "windows_x86_64_gnullvm 0.42.2",
 "windows_x86_64_msvc 0.42.2",
]

[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
 "windows_aarch64_gnullvm 0.48.0",
 "windows_aarch64_msvc 0.48.0",
 "windows_i686_gnu 0.48.0",
 "windows_i686_msvc 0.48.0",
 "windows_x86_64_gnu 0.48.0",
 "windows_x86_64_gnullvm 0.48.0",
 "windows_x86_64_msvc 0.48.0",
]

[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"

[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"

[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"

[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"

[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"

[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"

[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"

[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"

[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"

[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"

[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"

[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"

[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"

D Cargo.toml => Cargo.toml +0 -9
@@ 1,9 0,0 @@
[package]
name = "effem"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dev-dependencies]
tempfile = "3.5"
\ No newline at end of file

D LICENSE => LICENSE +0 -21
@@ 1,21 0,0 @@
ANTI-CAPITALIST SOFTWARE LICENSE (v 1.4)

Copyright © Johan Johansson

This is anti-capitalist software, released for free use by individuals and organizations that do not operate by capitalist principles.

Permission is hereby granted, free of charge, to any person or organization (the "User") obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, merge, distribute, and/or sell copies of the Software, subject to the following conditions:

1. The above copyright notice and this permission notice shall be included in all copies or modified versions of the Software.

2. The User is one of the following:
a. An individual person, laboring for themselves
b. A non-profit organization
c. An educational institution
d. An organization that seeks shared profit for all of its members, and allows non-members to set the cost of their labor

3. If the User is an organization with owners, then all owners are workers and all workers are owners with equal equity and/or equal vote.

4. If the User is an organization, then the User is not law enforcement or military, or working for or under either.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file

M README.md => README.md +2 -7
@@ 1,8 1,3 @@
# The Effem Programming Language
# [↠ Kapreolo ↞](https://sr.ht/~jojo/kapreolo/)

Goal: a general purpose programming language with
- a functional focus
- algebraic effects
- lispy syntax
- on-demand compilation
- Go-like memory management
is the new home of the language formerly known as Effem

D src/abase.rs => src/abase.rs +0 -176
@@ 1,176 0,0 @@
//! Base -- the lowest level IR in Effem before generating the output machine code.

use crate::prelude::*;
use base::*;
use fem::{Expr as FExpr, ExprKind as FExprKind, Type};
use std::collections::{HashMap, HashSet};

pub fn abase_def(cache: &mut Cache, rhs: &FExpr) -> Result<GlobDef> {
    if let FExprKind::Fun(params, body) = &rhs.kind {
        AbaseDef::new(cache).run_fun(params, body).map(GlobDef::FunDef)
    } else {
        AbaseDef::new(cache).run_var(rhs).map(GlobDef::VarDef)
    }
}

#[derive(Debug)]
struct AbaseDef<'c> {
    cache: &'c mut Cache,
    children: HashSet<DefId>,
    locals: HashMap<LocalId, Operand>,
    stm_arena: Vec<Stm>,
    block_arena: Vec<Block>,
    current_stms: Vec<StmRef>,
}

impl<'c> AbaseDef<'c> {
    fn new(cache: &'c mut Cache) -> Self {
        Self {
            cache,
            children: HashSet::new(),
            locals: HashMap::new(),
            stm_arena: vec![],
            block_arena: vec![],
            current_stms: vec![],
        }
    }

    fn run_var(mut self, rhs: &FExpr) -> Result<GVarDef> {
        let root = self.enter_block(|self_| self_.abase(rhs))?;
        // TODO: assert ret is not Local
        Ok(GVarDef {
            children: self.children,
            body: Body { stm_arena: self.stm_arena, block_arena: self.block_arena, root },
        })
    }

    fn run_fun(mut self, params: &[(PubIdent, LocalId)], body: &FExpr) -> Result<FunDef> {
        self.locals.extend(params.iter().enumerate().map(|(i, &(_, lid @ LocalId(i2)))| {
            debug_assert_eq!(i, i2 as usize);
            (lid, Operand::Param(ParamRef(i2)))
        }));
        let root = self.enter_block(|self_| self_.abase(body))?;
        Ok(FunDef {
            children: self.children,
            params: params.iter().map(|_| ()).collect(),
            body: Body { stm_arena: self.stm_arena, block_arena: self.block_arena, root },
        })
    }

    fn abase(&mut self, expr: &FExpr) -> Result<Flow> {
        use crate::fem::ExprKind::*;
        fn wrap(rand: Operand) -> Flow {
            Flow::Produce(Operation::Wrap(rand))
        }
        match &expr.kind {
            Bool(x) => Ok(wrap(Operand::Const(Const::Bool(*x)))),
            &Int(x) => Ok(wrap(Operand::Const(match expr.typ {
                Type::Int { width: 8, signed: true } => Const::I8(x as i8),
                Type::Int { width: 16, signed: true } => Const::I16(x as i16),
                Type::Int { width: 32, signed: true } => Const::I32(x as i32),
                Type::Int { width: 64, signed: true } => Const::I64(x as i64),
                Type::Int { width: 8, signed: false } => Const::N8(x as u8),
                Type::Int { width: 16, signed: false } => Const::N16(x as u16),
                Type::Int { width: 32, signed: false } => Const::N32(x as u32),
                Type::Int { width: 64, signed: false } => Const::N64(x as u64),
                Type::Int { width, signed } => panic!("ice: bad integer type with width={width}, signed={signed}"),
                Type::ISize => Const::Int(x as isize),
                Type::NSize => Const::Nat(x as usize),
                ref t => panic!("ice: integer expr has non-integer type {t}"),
            }))),
            F64(x) => Ok(wrap(Operand::Const(Const::F64(*x)))),
            Fun(ps, b) => Ok(wrap(self.abase_fun(ps, b)?)),
            App(f, xs) => match &f.kind {
                Var(ResName { res: Res::Prim(p) }) => match p {
                    Prim::Add => self.abase_binop(xs, Binop::Add),
                    Prim::Sub => self.abase_binop(xs, Binop::Sub),
                    Prim::Mul => self.abase_binop(xs, Binop::Mul),
                    Prim::Quot => self.abase_binop(xs, Binop::Quot),
                    Prim::Rem => self.abase_binop(xs, Binop::Rem),
                    Prim::Lt => self.abase_binop(xs, Binop::Lt),
                    Prim::Gt => self.abase_binop(xs, Binop::Gt),
                    Prim::Leq => self.abase_binop(xs, Binop::Leq),
                    Prim::Geq => self.abase_binop(xs, Binop::Geq),
                    Prim::Eq => self.abase_binop(xs, Binop::Eq),
                    Prim::Neq => self.abase_binop(xs, Binop::Neq),
                },
                _ => {
                    let f = self.abase(f)?;
                    let f = self.let_anon_soft(f)?;
                    let xs = xs
                        .iter()
                        .map(|x| {
                            let x = self.abase(x)?;
                            self.let_anon_soft(x)
                        })
                        .collect::<Result<Vec<Operand>>>()?;
                    Ok(Operation::Call(f, xs))
                }
            }
            .map(Flow::Produce),
            Var(ResName { res: Res::Def(id) }) => Ok(wrap(Operand::Global(*id))),
            &Var(ResName { res: Res::Local(id) }) => Ok(wrap(self.locals[&id])),
            Var(ResName { res: Res::Prim(_) }) => todo!(), // TODO: generate a closure around the op or smth
            Var(ResName { res: Res::Module(_) }) =>
                panic!("ice: found module id in expr context when abasing. Should've been caught by type checker."),
            If(pred, conseq, alt) => {
                let pred_a = self.abase(pred)?;
                let pred_a = self.let_anon_soft(pred_a)?;
                let conseq_a = self.enter_block(|self_| self_.abase(conseq))?;
                let alt_a = self.enter_block(|self_| self_.abase(alt))?;
                Ok(Flow::Diverge(Diverge::If(pred_a, conseq_a, alt_a)))
            }
        }
    }

    fn abase_binop(&mut self, xs: &[FExpr], op: Binop) -> Result<Operation> {
        let x = self.abase(&xs[0])?;
        let x = self.let_anon_soft(x)?;
        let y = self.abase(&xs[1])?;
        let y = self.let_anon_soft(y)?;
        Ok(Operation::Binop(op, x, y))
    }

    // TODO: closures. capture free vars etc.
    fn abase_fun(&mut self, params: &[(PubIdent, LocalId)], body: &FExpr) -> Result<Operand> {
        let fdef = AbaseDef::new(self.cache).run_fun(params, body)?;
        let fid = self.cache.insert_base_anon(GlobDef::FunDef(fdef));
        self.children.insert(fid);
        Ok(Operand::Global(fid))
    }

    fn enter_block(&mut self, f: impl FnOnce(&mut Self) -> Result<Flow>) -> Result<BlockRef> {
        let old_stms = std::mem::take(&mut self.current_stms);
        let tail = f(self)?;
        let new_stms = std::mem::replace(&mut self.current_stms, old_stms);
        Ok(self.add_block(Block { stms: new_stms, tail }))
    }

    fn add_block(&mut self, block: Block) -> BlockRef {
        let i = self.block_arena.len();
        assert!(i < u32::MAX as usize);
        self.block_arena.push(block);
        BlockRef(i as u32)
    }

    fn let_anon_soft(&mut self, rhs: Flow) -> Result<Operand> {
        match rhs {
            Flow::Produce(Operation::Wrap(rand)) => Ok(rand),
            _ => self.let_anon_hard(rhs),
        }
    }

    fn let_anon_hard(&mut self, rhs: Flow) -> Result<Operand> {
        let lhs = self.add_stm(Stm::Let { rhs });
        Ok(Operand::Var(lhs))
    }

    fn add_stm(&mut self, stm: Stm) -> StmRef {
        let i = self.stm_arena.len();
        assert!(i < u32::MAX as usize);
        self.stm_arena.push(stm);
        let stm_ref = StmRef(i as u32);
        self.current_stms.push(stm_ref);
        stm_ref
    }
}

D src/base.rs => src/base.rs +0 -402
@@ 1,402 0,0 @@
use crate::prelude::*;
use std::collections::HashSet;
use std::fmt::{Display, Formatter, Result as FResult, Write as FWrite};

#[derive(Clone, Debug)]
pub enum GlobDef {
    FunDef(FunDef),
    VarDef(GVarDef),
}

#[derive(Clone, Debug)]
pub struct FunDef {
    // Nested definitions (including anonymous functions)
    pub children: HashSet<DefId>,
    pub params: Vec<()>,
    pub body: Body,
}

#[derive(Clone, Debug)]
pub struct GVarDef {
    pub children: HashSet<DefId>,
    pub body: Body,
}

#[derive(Debug, Clone)]
pub struct Body {
    pub stm_arena: Vec<Stm>,
    pub block_arena: Vec<Block>,
    pub root: BlockRef,
}

#[derive(Debug, Clone)]
pub struct Block {
    pub stms: Vec<StmRef>,
    pub tail: Flow,
}

#[derive(Debug, Clone, Copy)]
pub struct BlockRef(pub u32);

#[derive(Debug, Clone)]
pub enum Stm {
    Let { rhs: Flow },
    // Store { val: Operand, dst: Operand },
    // VoidCall { proc: Operand, out: Option<Operand>, args: Vec<Pass<Operand>> }
    // SLoop (Loop ())
    // SBranch (Branch ())
}

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

#[derive(Debug, Clone)]
pub enum Flow {
    Diverge(Diverge),
    // Void,
    Produce(Operation),
    // Continue(Vec<Operand>) // only valid in loops
}

#[derive(Debug, Clone)]
pub enum Diverge {
    If(Operand, BlockRef, BlockRef),
}

// pub struct Loop {
//     pub params: Vec<(LocalId, Operand)>,
//     pub body: Block,
// }

#[derive(Clone, Debug)]
pub enum Operation {
    Binop(Binop, Operand, Operand),
    Call(Operand, Vec<Operand>),
    Wrap(Operand),
}

#[derive(Debug, Clone, Copy)]
pub enum Binop {
    Add,
    Sub,
    Mul,
    Quot,
    Rem,
    Lt,
    Gt,
    Leq,
    Geq,
    Eq,
    Neq,
}

#[derive(Debug, Clone, Copy)]
pub enum Operand {
    Const(Const),
    // Extern Extern
    Var(StmRef),
    Param(ParamRef),
    Global(DefId),
}

#[derive(Debug, Clone, Copy)]
pub struct ParamRef(pub u32);

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Const {
    Bool(bool),
    I8(i8),
    I16(i16),
    I32(i32),
    I64(i64),
    Int(isize),
    N8(u8),
    N16(u16),
    N32(u32),
    N64(u64),
    Nat(usize),
    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 {
            fn from(x: $t) -> Const {
                $c(x)
            }
        }
    };
}

impl_const_from!(bool, Const::Bool);
impl_const_from!(i8, Const::I8);
impl_const_from!(i16, Const::I16);
impl_const_from!(i32, Const::I32);
impl_const_from!(i64, Const::I64);
impl_const_from!(isize, Const::Int);
impl_const_from!(u8, Const::N8);
impl_const_from!(u16, Const::N16);
impl_const_from!(u32, Const::N32);
impl_const_from!(u64, Const::N64);
impl_const_from!(usize, Const::Nat);
impl_const_from!(f64, Const::F64);

macro_rules! impl_const_arithm_binop {
    ($op_trait:ty, $op_name:ident) => {
        impl $op_trait for Const {
            type Output = Option<Const>;
            fn $op_name(self, other: Self) -> Self::Output {
                match (self, other) {
                    (Const::I8(x), Const::I8(y)) => Some(x.$op_name(y).into()),
                    (Const::I16(x), Const::I16(y)) => Some(x.$op_name(y).into()),
                    (Const::I32(x), Const::I32(y)) => Some(x.$op_name(y).into()),
                    (Const::I64(x), Const::I64(y)) => Some(x.$op_name(y).into()),
                    (Const::Int(x), Const::Int(y)) => Some(x.$op_name(y).into()),
                    (Const::N8(x), Const::N8(y)) => Some(x.$op_name(y).into()),
                    (Const::N16(x), Const::N16(y)) => Some(x.$op_name(y).into()),
                    (Const::N32(x), Const::N32(y)) => Some(x.$op_name(y).into()),
                    (Const::N64(x), Const::N64(y)) => Some(x.$op_name(y).into()),
                    (Const::Nat(x), Const::Nat(y)) => Some(x.$op_name(y).into()),
                    (Const::F64(x), Const::F64(y)) => Some(x.$op_name(y).into()),
                    _ => None,
                }
            }
        }
    };
}

impl_const_arithm_binop!(std::ops::Add, add);
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})"),
        }
    }
}

D src/cache.rs => src/cache.rs +0 -357
@@ 1,357 0,0 @@
use crate::prelude::*;
use parse::{Expr as PExpr, Module as PModule};
use resolve::Expr as RExpr;
use std::borrow::Borrow;
use std::collections::hash_map::{Entry, HashMap};
use std::hash::Hash;
use std::path::{Path, PathBuf};
use std::rc::Rc;

#[derive(Debug)]
pub struct Cache {
    root_dir: PathBuf,
    n_defs: u32,
    n_modules: u32,
    sources: HashMap<FileId, String>,
    file_paths: HashMap<FileId, PathBuf>,
    module_files: HashMap<ModuleId, FileId>,
    parsed_modules: HashMap<ModuleId, HashMap<String, (Loc, PExpr)>>,
    parent_modules: HashMap<DefId, ModuleId>,
    resolved_names: HashMap<FullName<String>, Res>,
    resolved_names_rev: HashMap<Res, FullName<String>>,
    module_local_resolved_names: HashMap<ModuleId, HashMap<String, Res>>,
    resolveds: HashMap<DefId, RExpr>,
    sigs: HashMap<DefId, Fetch<fem::Type>>,
    checkeds: HashMap<DefId, Fetch<fem::Expr>>,
    abaseds: HashMap<DefId, base::GlobDef>,
    evaluateds: HashMap<DefId, base::Operand>,
}

impl Cache {
    pub fn for_package_in(root_dir: &Path) -> Self {
        Self {
            root_dir: root_dir.to_owned(),
            n_defs: 0,
            n_modules: 0,
            file_paths: HashMap::new(),
            sources: HashMap::new(),
            module_files: HashMap::new(),
            parsed_modules: HashMap::new(),
            resolved_names: HashMap::new(),
            resolved_names_rev: HashMap::new(),
            parent_modules: HashMap::new(),
            module_local_resolved_names: HashMap::new(),
            resolveds: HashMap::new(),
            sigs: HashMap::new(),
            checkeds: HashMap::new(),
            abaseds: HashMap::new(),
            evaluateds: HashMap::new(),
        }
    }

    pub fn fetch_evaluated_at(&mut self, fundef_query: DefId, args: &[base::Operand]) -> Result<base::Operand> {
        #[cfg(debug_assertions)]
        for arg in args.iter().filter(|arg| !matches!(arg, base::Operand::Const(_) | base::Operand::Global(_))) {
            panic!("ice: Local scope operand {arg:?} as arg to another function");
        }

        let _ = self.fetch_evaluated(fundef_query)?;
        match self.abaseds[&fundef_query] {
            base::GlobDef::FunDef(ref fdef) => eval::eval_fun_at(self, &fdef.clone(), args),
            ref def => panic!("ice: {def:?} is not a function def. Cannot evaluate at arguments."),
        }
    }

    pub fn fetch_evaluated(&mut self, def_query: DefId) -> Result<&base::Operand> {
        if self.evaluateds.contains_key(&def_query) {
            Ok(self.evaluateds.get(&def_query).unwrap())
        } else {
            let base = self.fetch_base(def_query)?.clone();
            let val = match base {
                base::GlobDef::VarDef(def) => eval::eval_var(self, &def)?,
                base::GlobDef::FunDef(_) => base::Operand::Global(def_query),
            };
            self.evaluateds.insert(def_query, val);
            Ok(&self.evaluateds[&def_query])
        }
    }

    pub fn fetch_base(&mut self, def_query: DefId) -> Result<&base::GlobDef> {
        Ok(if self.abaseds.contains_key(&def_query) {
            &self.abaseds[&def_query]
        } else {
            let checked: fem::Expr = match self.fetch_checked(def_query)? {
                Fetch::Done(x) => x.clone(),
                Fetch::Sentinel => panic!("ice: sentinel value when fetching checked in fetch_base"),
            };
            let base = abase::abase_def(self, &checked)?;
            self.abaseds.insert(def_query, base);
            &self.abaseds[&def_query]
        })
    }

    pub fn insert_base_anon(&mut self, def: base::GlobDef) -> DefId {
        let def_id = self.gen_def_id();
        self.abaseds.insert(def_id, def);
        def_id
    }

    pub fn fetch_checked(&mut self, def_query: DefId) -> Result<Fetch<&fem::Expr>> {
        if self.checkeds.contains_key(&def_query) {
            Ok(self.checkeds[&def_query].as_ref())
        } else {
            self.checkeds.insert(def_query, Fetch::Sentinel);
            let desugared: RExpr = self.fetch_desugared(def_query)?.clone();
            let checked = check::check_def(self, &desugared)?;
            self.checkeds.insert(def_query, Fetch::Done(checked));
            Ok(self.checkeds[&def_query].as_ref())
        }
    }

    pub fn fetch_sig(&mut self, def_query: DefId) -> Result<Fetch<&fem::Type>> {
        if self.sigs.contains_key(&def_query) {
            Ok(self.sigs[&def_query].as_ref())
        } else {
            self.sigs.insert(def_query, Fetch::Sentinel);
            let desugared: RExpr = self.fetch_desugared(def_query)?.clone();
            if let resolve::ExprKind::Annot(_, sig) = &desugared.kind {
                if let Some(sig) = sig.to_fem() {
                    self.sigs.insert(def_query, Fetch::Done(sig));
                    return Ok(self.sigs[&def_query].as_ref());
                }
            }
            match self.fetch_checked(def_query)? {
                Fetch::Done(checked) => {
                    let typ = checked.typ.clone();
                    self.sigs.insert(def_query, Fetch::Done(typ));
                    Ok(self.sigs[&def_query].as_ref())
                }
                Fetch::Sentinel => Ok(Fetch::Sentinel),
            }
        }
    }

    pub fn fetch_desugared(&mut self, def_query: DefId) -> Result<&RExpr> {
        self.fetch_resolved(def_query)
        // Ok(if self.desugareds.contains_key(def_query) {
        //     &self.desugareds[def_query]
        // } else {
        //     let resolved = self.fetch_resolved(def_query)?.clone();
        //     let desugared = desugar(&resolved)?;
        //     self.desugareds.insert(def_query.clone(), desugared);
        //     self.desugareds.get(def_query).unwrap()
        // })
    }

    pub fn fetch_resolved(&mut self, def_id: DefId) -> Result<&RExpr> {
        if self.resolveds.contains_key(&def_id) {
            Ok(&self.resolveds[&def_id])
        } 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.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)?;
            self.resolveds.insert(def_id, resolved);
            Ok(&self.resolveds[&def_id])
        }
    }

    pub fn fetch_module_local_resolved_names(&mut self, module_id: ModuleId) -> Result<&HashMap<String, Res>> {
        static PRELUDE: &[(&str, Res)] = &[
            ("+", Res::Prim(Prim::Add)),
            ("-", Res::Prim(Prim::Sub)),
            ("*", Res::Prim(Prim::Mul)),
            ("quot", Res::Prim(Prim::Quot)),
            ("rem", Res::Prim(Prim::Rem)),
            ("<", Res::Prim(Prim::Lt)),
            (">", Res::Prim(Prim::Gt)),
            ("<=", Res::Prim(Prim::Leq)),
            (">=", Res::Prim(Prim::Geq)),
            ("=", Res::Prim(Prim::Eq)),
            ("/=", Res::Prim(Prim::Neq)),
        ];

        if self.module_local_resolved_names.contains_key(&module_id) {
            Ok(&self.module_local_resolved_names[&module_id])
        } else {
            let def_idents: Vec<String> = self.fetch_parsed_module(module_id)?.keys().cloned().collect();
            let ids = PRELUDE
                .iter()
                .map(|(ident, res)| (ident.to_string(), *res))
                .chain(
                    def_idents
                        .into_iter()
                        .map(|def_ident| (def_ident.clone(), Res::Def(self.gen_def_resolution(def_ident, module_id)))),
                )
                .collect();
            self.module_local_resolved_names.insert(module_id, ids);
            Ok(&self.module_local_resolved_names[&module_id])
        }
    }

    pub fn fetch_global_resolved_name(&mut self, name: &FullName<PubIdent>) -> Result<Res> {
        use ResolveErr::*;
        let sname = name.clone().strip_locs();
        if sname.first() == "prim" {
            if name.len() != 2 {
                todo!() // Err
            }
            match sname.get(1).unwrap().as_str() {
                "+" => Ok(Res::Prim(Prim::Add)),
                "-" => Ok(Res::Prim(Prim::Sub)),
                "*" => Ok(Res::Prim(Prim::Mul)),
                "quot" => Ok(Res::Prim(Prim::Quot)),
                "rem" => Ok(Res::Prim(Prim::Rem)),
                "<" => Ok(Res::Prim(Prim::Lt)),
                ">" => Ok(Res::Prim(Prim::Gt)),
                "<=" => Ok(Res::Prim(Prim::Leq)),
                ">=" => Ok(Res::Prim(Prim::Geq)),
                "=" => Ok(Res::Prim(Prim::Eq)),
                "/=" => Ok(Res::Prim(Prim::Neq)),
                _ => todo!(), // Err
            }
        } else if let Some(&res) = self.resolved_names.get(&sname) {
            Ok(res)
        } else if let "pkg" | "own" = sname.first().as_str() {
            let path = self.module_name_to_path(&sname);
            if path.exists() {
                return Ok(Res::Module(self.gen_module_resolution(name.clone().strip_locs(), path)));
            }
            let (def_ident, module_name) = mapr(name.clone().split_last(), Option::unwrap);
            match self.fetch_global_resolved_name(&module_name)? {
                Res::Module(module_id) => {
                    let module = self.fetch_parsed_module(module_id)?;
                    if module.contains_key(&def_ident.s) {
                        return Ok(Res::Def(self.gen_def_resolution(def_ident.s, module_id)));
                    }
                }
                _ => return Err(ParentIsNotModule(def_ident.loc, module_name).into()),
            }
            Err(Unresolved(name.clone()).into())
        } else {
            Err(InvalidRootNamespace(name.get(0).unwrap().clone()).into())
        }
    }

    fn module_name_to_path(&self, mname: &FullName<String>) -> PathBuf {
        if mname.first() == "pkg" {
            todo!()
        } else {
            debug_assert!(mname.first() == "own");
            let mut path = self.root_dir.to_path_buf();
            if mname.len() == 1 {
                path.push("lib.fm");
            } else {
                path.extend(mname.iter().skip(1));
                path.set_extension("fm");
            }
            path
        }
    }

    fn gen_module_resolution(&mut self, module_name: FullName<String>, path: PathBuf) -> ModuleId {
        assert!(self.n_modules < u32::MAX);
        let mid = ModuleId(self.n_modules);
        self.n_modules += 1;
        assert!(self.file_paths.len() < u32::MAX as usize);
        let fid = FileId(self.file_paths.len() as u32);
        self.file_paths.insert(fid, path);
        self.module_files.insert(mid, fid);
        self.resolved_names.insert(module_name.clone(), Res::Module(mid));
        self.resolved_names_rev.insert(Res::Module(mid), module_name);
        mid
    }

    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.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:?}"),
            None => {
                let def_id = self.gen_def_id();
                let res = Res::Def(def_id);
                self.resolved_names.insert(def_name.clone(), res);
                self.resolved_names_rev.insert(res, def_name.clone());
                self.parent_modules.insert(def_id, module_id);
                def_id
            }
        }
    }

    fn gen_def_id(&mut self) -> DefId {
        assert!(self.n_defs < u32::MAX);
        self.n_defs += 1;
        self.n_defs - 1
    }

    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 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")
    }

    pub fn fetch_parsed_module(&mut self, module_id: ModuleId) -> Result<&HashMap<String, (Loc, PExpr)>> {
        if !self.parsed_modules.contains_key(&module_id) {
            // TODO: nested modules in the same file?
            let file_id = *self
                .module_files
                .get(&module_id)
                .expect("ice: there should be a corresponding file id for each module id");
            let src = self.fetch_source(file_id)?;
            let tokens = lex::lex(Some(file_id), src)?;
            let parsed = parse::parse(Some(file_id), &tokens)?;
            let mut dedup: HashMap<String, (Loc, PExpr)> = HashMap::new();
            for (lhs, rhs) in parsed.defs {
                let (lhs, loc) = (lhs.substr_in(src), Loc::new(Some(file_id), lhs.start));
                if dedup.insert(lhs.to_string(), (loc, rhs)).is_some() {
                    return Err(Error::Resolve(ResolveErr::DupDef(loc)));
                }
            }
            self.parsed_modules.insert(module_id, dedup);
        }
        Ok(&self.parsed_modules[&module_id])
    }

    pub fn fetch_source(&mut self, file_id: FileId) -> Result<&str> {
        if !self.sources.contains_key(&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 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 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)]
pub enum Fetch<T> {
    Done(T),
    Sentinel,
}

impl<T> Fetch<T> {
    pub fn as_ref(&self) -> Fetch<&T> {
        match *self {
            Fetch::Done(ref x) => Fetch::Done(x),
            Fetch::Sentinel => Fetch::Sentinel,
        }
    }
}

D src/check.rs => src/check.rs +0 -222
@@ 1,222 0,0 @@
use crate::prelude::*;
use diag::TypeErr::*;
use fem::*;
use resolve::{Expr as RExpr, Type as RType};
use std::collections::HashMap;

pub fn check_def(cache: &mut Cache, body: &RExpr) -> Result<Expr> {
    CheckDef::new(cache).run(body)
}

struct CheckDef<'c> {
    cache: &'c mut Cache,
    scopes: Vec<HashMap<LocalId, Type>>,
}

impl<'c> CheckDef<'c> {
    fn new(cache: &'c mut Cache) -> Self {
        Self { cache, scopes: vec![] }
    }

    fn run(mut self, body: &RExpr) -> Result<Expr> {
        self.infer(body)
    }

    fn infer(&mut self, expr: &RExpr) -> Result<Expr> {
        use resolve::ExprKind as Rek;
        match &expr.kind {
            &Rek::Bool(x) => Ok(Expr { typ: Type::Bool, kind: ExprKind::Bool(x) }),
            Rek::Int(x) => Ok(Expr {
                typ: if (isize::MIN as i128..=isize::MAX as i128).contains(x) {
                    Type::ISize
                } else if (i64::MIN as i128..=i64::MAX as i128).contains(x) {
                    Type::Int { width: 64, signed: true }
                } else {
                    Type::Int { width: 128, signed: true }
                },
                kind: ExprKind::Int(*x),
            }),
            &Rek::F64(x) => Ok(Expr { typ: Type::F64, kind: ExprKind::F64(x) }),
            Rek::Fun(ps, b) if ps.is_empty() => {
                let bi = self.infer(b)?;
                Ok(Expr { typ: Type::Fun(vec![], Box::new(bi.typ.clone())), kind: ExprKind::Fun(vec![], Box::new(bi)) })
            }
            Rek::Fun(_, _) => err(InferLambda(expr.loc)),
            Rek::App(f, args) => match f.kind {
                Rek::Fun(_, _) | Rek::Var(ResName { res: Res::Prim(_) }) => {
                    let argsi = args.iter().map(|a| self.infer(a)).collect::<Result<Vec<_>>>()?;
                    let t_fexpected =
                        RType::Fun(argsi.iter().map(|e| e.typ.to_parsed()).collect(), Box::new(RType::Hole));
                    let fc = self.check(f, &t_fexpected)?;
                    let typ = match &fc.typ {
                        Type::Fun(_, t_ret) => (**t_ret).clone(),
                        _ => panic!("ice: checked function of application should be Type::Fun, is {:?}", fc.typ),
                    };
                    Ok(Expr { typ, kind: ExprKind::App(Box::new(fc), argsi) })
                }
                _ => {
                    let fi = self.infer(f)?;
                    if let Type::Fun(ps, r) = &fi.typ {
                        if ps.len() == args.len() {
                            let argsc = ps
                                .iter()
                                .zip(args)
                                .map(|(p, a)| self.check(a, &p.to_parsed()))
                                .collect::<Result<Vec<_>>>()?;
                            Ok(Expr { typ: (**r).clone(), kind: ExprKind::App(Box::new(fi), argsc) })
                        } else {
                            err(ArityMisApp { loc: expr.loc, params: ps.len(), args: args.len() })
                        }
                    } else {
                        err(AppNonFun(expr.loc, fi.typ))
                    }
                }
            },
            Rek::Var(r @ ResName { res: Res::Def(id) }) => {
                let typ = match self.cache.fetch_sig(*id)? {
                    Fetch::Done(typ) => typ.clone(),
                    Fetch::Sentinel => return err(InferRecursive(expr.loc, *id)),
                };
                Ok(Expr { typ, kind: ExprKind::Var(r.clone()) })
            }
            Rek::Var(r @ ResName { res: Res::Local(id) }) => match self.get_local(*id) {
                Some(t) => Ok(Expr { typ: t.clone(), kind: ExprKind::Var(r.clone()) }),
                None => panic!("ice: undefined local var of id {id:?}"),
            },
            Rek::Var(r @ ResName { res: Res::Prim(Prim::Add | Prim::Sub | Prim::Mul | Prim::Quot | Prim::Rem) }) =>
                Ok(Expr {
                    typ: Type::Fun(vec![Type::ISize, Type::ISize], Box::new(Type::ISize)),
                    kind: ExprKind::Var(r.clone()),
                }),
            Rek::Var(
                r @ ResName { res: Res::Prim(Prim::Lt | Prim::Gt | Prim::Leq | Prim::Geq | Prim::Eq | Prim::Neq) },
            ) => Ok(Expr {
                typ: Type::Fun(vec![Type::ISize, Type::ISize], Box::new(Type::Bool)),
                kind: ExprKind::Var(r.clone()),
            }),
            Rek::Var(ResName { res: Res::Module(id) }) => err(ModuleIsNotExpr(expr.loc, *id)),
            Rek::Annot(e, t) => self.check(e, t),
            Rek::If(p, c, a) => {
                let pc = self.check(p, &RType::Bool)?;
                let ci = self.infer(c)?;
                // TODO: On error, add context / note about what type was inferred for the prev branch
                let ac = self.check(a, &ci.typ.to_parsed())?;
                Ok(Expr { typ: ci.typ.clone(), kind: ExprKind::If(Box::new(pc), Box::new(ci), Box::new(ac)) })
            }
        }
    }

    fn check(&mut self, expr: &RExpr, expected: &RType) -> Result<Expr> {
        use resolve::ExprKind as Rek;
        match &expr.kind {
            _ if matches!(expected, RType::Hole) => self.infer(expr),
            &Rek::Int(x) => {
                match expected {
                    RType::Int { width: 128, signed: true } => (),
                    RType::Int { width: 128, signed: false } if 0 <= x => (),
                    RType::Int { width, signed: true } if -(1 << (width - 1)) <= x && x < (1 << (width - 1)) => (),
                    RType::Int { width, signed: false } if 0 <= x && x < (1 << width) => (),
                    RType::ISize if isize::MIN as i128 <= x && x <= isize::MAX as i128 => (),
                    RType::NSize if 0 <= x && x as u128 <= usize::MAX as u128 => (),
                    RType::Int { .. } | RType::ISize | RType::NSize =>
                        return err(ExpectedFoundOutOfRange(expr.loc, expected.clone())),
                    _ => return err(ExpectedFound(expr.loc, expected.clone(), self.infer(expr)?.typ.to_parsed())),
                };
                Ok(Expr { typ: expected.to_fem().unwrap(), kind: ExprKind::Int(x) })
            }
            Rek::Fun(ps, b) =>
                if let RType::Fun(tps, tr) = expected {
                    if ps.len() != tps.len() {
                        return err(ArityMisLambda { loc: expr.loc, check: tps.len(), lit: ps.len() });
                    }
                    let tps = ps
                        .iter()
                        .zip(tps)
                        .map(|((ident, _), t)| t.to_fem().ok_or_else(|| InferLambdaParam(ident.clone())))
                        .collect::<std::result::Result<Vec<_>, _>>()?;
                    self.scopes.push(ps.iter().map(|(_, id)| *id).zip(tps.iter().cloned()).collect());
                    let bc = self.check(b, tr)?;
                    self.scopes.pop();
                    Ok(Expr {
                        typ: Type::Fun(tps, Box::new(bc.typ.clone())),
                        kind: ExprKind::Fun(ps.clone(), Box::new(bc)),
                    })
                } else {
                    err(ExpectedFoundLambda(expr.loc, expected.clone()))
                },
            Rek::Var(r @ ResName { res: Res::Prim(prim) }) => match prim {
                Prim::Add | Prim::Sub | Prim::Mul | Prim::Quot | Prim::Rem =>
                    if let RType::Fun(tps, tr) = expected {
                        if tps.len() != 2 {
                            return err(ArityMisLambda { loc: expr.loc, check: tps.len(), lit: 2 });
                        }
                        let tx = tps.iter().find(|t| !matches!(t, RType::Hole)).unwrap_or(tr).or(&RType::ISize);
                        if tps.iter().any(|t| !subtype(tx, t)) || !subtype(tx, tr) {
                            return err(ExpectedFoundArithmBinop(expr.loc, expected.clone()));
                        }
                        let tx = tx.to_fem().unwrap();
                        Ok(Expr {
                            typ: Type::Fun(vec![tx.clone(), tx.clone()], Box::new(tx)),
                            kind: ExprKind::Var(r.clone()),
                        })
                    } else {
                        err(ExpectedFoundArithmBinop(expr.loc, expected.clone()))
                    },
                Prim::Lt | Prim::Gt | Prim::Leq | Prim::Geq | Prim::Eq | Prim::Neq =>
                    if let RType::Fun(tps, tr) = expected {
                        if tps.len() != 2 {
                            return err(ArityMisLambda { loc: expr.loc, check: tps.len(), lit: 2 });
                        }
                        let tp = tps.iter().find(|t| !matches!(t, RType::Hole)).unwrap_or(&RType::ISize);
                        if tps.iter().any(|t| !subtype(tp, t)) || !subtype(&RType::Bool, tr) {
                            return err(ExpectedFoundLogicBinop(expr.loc, expected.clone()));
                        }
                        let tp = tp.to_fem().unwrap();
                        Ok(Expr {
                            typ: Type::Fun(vec![tp.clone(), tp.clone()], Box::new(Type::Bool)),
                            kind: ExprKind::Var(r.clone()),
                        })
                    } else {
                        err(ExpectedFoundLogicBinop(expr.loc, expected.clone()))
                    },
            },
            Rek::Annot(e, t) =>
                if subtype(t, expected) {
                    self.check(e, t)
                } else {
                    err(ExpectedFoundAnnot(expr.loc, expected.clone(), t.clone()))
                },
            Rek::If(p, c, a) => {
                let p_c = self.check(p, &RType::Bool)?;
                let c_c = self.check(c, expected)?;
                let a_c = self.check(a, expected)?;
                Ok(Expr { typ: c_c.typ.clone(), kind: ExprKind::If(Box::new(p_c), Box::new(c_c), Box::new(a_c)) })
            }
            _ => {
                let expri = self.infer(expr)?;
                if subtype(&expri.typ.to_parsed(), expected) {
                    Ok(expri)
                } else {
                    err(ExpectedFound(expr.loc, expected.clone(), expri.typ.to_parsed()))
                }
            }
        }
    }

    fn get_local(&self, id: LocalId) -> Option<&Type> {
        self.scopes.iter().rev().find_map(|locals| locals.get(&id))
    }
}

fn subtype(t: &RType, u: &RType) -> bool {
    match (t, u) {
        (_, RType::Hole) => true,
        (RType::Fun(tps, tr), RType::Fun(ups, ur)) =>
            tps.iter().zip(ups).all(|(t, u)| subtype(t, u)) && subtype(tr, ur),
        _ => t == u,
    }
}

fn err<A>(c: TypeErr) -> Result<A> {
    Err(Error::Type(c))
}

D src/desugar.rs => src/desugar.rs +0 -11
@@ 1,11 0,0 @@
use crate::prelude::*;
use fem::*;

// pub fn desugar(expr: &ast::RExpr) -> anyhow::Result<Expr> {
//     Ok(match expr {
//         ast::Expr::F64(x) => Expr::F64(*x),
//         ast::Expr::Var(x) => Expr::Var(x.clone()),
//         ast::Expr::App(f, xs) =>
//             Expr::App(Box::new(desugar(f)?), xs.iter().map(|x| desugar(x)).collect::<Result<_, _>>()?),
//     })
// }

D src/diag.rs => src/diag.rs +0 -365
@@ 1,365 0,0 @@
use crate::prelude::*;
use fem::Type as FType;
use resolve::Type as RType;
use std::collections::HashSet;
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>;

#[derive(Debug, Clone)]
pub enum Error {
    Type(TypeErr),
    Resolve(ResolveErr),
    Parse(ParseErr),
    Lex(LexErr),
}

impl Error {
    pub fn render(&self, cache: &mut Cache) -> String {
        match self {
            Error::Type(e) => e.render(cache),
            Error::Resolve(e) => e.render(cache),
            Error::Parse(e) => e.render(cache),
            Error::Lex(e) => e.render(cache),
        }
    }
}

impl From<TypeErr> for Error {
    fn from(x: TypeErr) -> Error {
        Error::Type(x)
    }
}
impl From<ResolveErr> for Error {
    fn from(x: ResolveErr) -> Error {
        Error::Resolve(x)
    }
}
impl From<ParseErr> for Error {
    fn from(x: ParseErr) -> Error {
        Error::Parse(x)
    }
}
impl From<LexErr> for Error {
    fn from(x: LexErr) -> Error {
        Error::Lex(x)
    }
}

#[derive(Debug, Clone)]
pub enum TypeErr {
    ExpectedFound(Loc, RType, RType),
    ExpectedFoundAnnot(Loc, RType, RType),
    ExpectedFoundLambda(Loc, RType),
    ExpectedFoundArithmBinop(Loc, RType),
    ExpectedFoundLogicBinop(Loc, RType),
    ExpectedFoundOutOfRange(Loc, RType),
    ArityMisLambda { loc: Loc, check: usize, lit: usize },
    ArityMisApp { loc: Loc, params: usize, args: usize },
    InferLambda(Loc),
    InferLambdaParam(PubIdent),
    AppNonFun(Loc, FType),
    ModuleIsNotExpr(Loc, ModuleId),
    InferRecursive(Loc, DefId),
}

impl TypeErr {
    pub fn render(&self, cache: &mut Cache) -> String {
        use TypeErr::*;
        match self {
            ExpectedFound(loc, e, f) => loc.error(cache, format!("Expected `{e}`, found `{f}`.")),
            ExpectedFoundAnnot(loc, e, f) => loc.error(cache, format!("Expected {e}, found {f} (according to annotation).")),
            ExpectedFoundLambda(loc, e) => loc.error(cache, format!("Expected {e}, found a function.")),
            ExpectedFoundArithmBinop(loc, e) => loc.error(cache, format!("Expected {e}, found an arithmetic binary operator.")),
            ExpectedFoundLogicBinop(loc, e) => loc.error(cache, format!("Expected {e}, found a logic binary operator.")),
            ExpectedFoundOutOfRange(loc, e) => loc.error(cache, format!("Expected {e}, found integer out of range for that type.")),
            ArityMisLambda { loc, check, lit } => loc.error(cache, format!("Arity mismatch in function parameters. Expectectations from the outside are that there be {check} parameters, but the function here instead has {lit}.")),
            ArityMisApp { loc, params, args } => loc.error(cache, format!("Arity mismatch in function application. Function specifies {params} parameters, but {args} arguments were given.")),
            InferLambda(loc) => loc.error(cache, "Can't infer type of lambdas without any additional type information (yet). Please surround the lambda in a type annotation to have the type checked instead of inferred."),
            InferLambdaParam(ident) => ident.loc.error(cache, "Can't infer type of lambda parameter (yet). Maybe surround the lambda in a type annotation to have the type checked instead of inferred."),
            AppNonFun(loc, t) => loc.error(cache, format!("Expected a function to apply, found a {t}.")),
            ModuleIsNotExpr(loc, _mid) => loc.error(cache, "Expected an expression, but this name resolves to a module."),
            InferRecursive(loc, _def_id) => loc.error(cache, "Recursion when inferring the type of this definition. Adding type signatures to top-level definitions will probably help.")
        }
    }
}

#[derive(Debug, Clone)]
pub enum ResolveErr {
    UndefInMod(PubIdent, ModuleId),
    InvalidRootNamespace(PubIdent),
    Unresolved(FullName<PubIdent>),
    ParentIsNotModule(Loc, FullName<PubIdent>),
    DupDef(Loc),
}

impl ResolveErr {
    pub fn render(&self, cache: &mut Cache) -> String {
        use ResolveErr::*;
        match self {
            UndefInMod(def, mid) => {
                let mname = cache.get_module_name(*mid);
                def.loc.error(cache, format!("Couldn't find definition `{def}` in module `{mname}`."))
            }
            InvalidRootNamespace(ns) =>
                ns.loc.error(cache, format!("Invalid module namespace `{ns}`. Expected one of `own`, `pkg`, `prim`.")),
            Unresolved(name) => name
                .loc()
                .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, "Duplicate definition of this name in this module."),
        }
    }
}

#[derive(Debug, Clone)]
pub struct ParseErr {
    pub loc: Loc,
    pub dist: usize,
    pub expecteds: HashSet<String>,
}

impl ParseErr {
    pub fn render(&self, cache: &mut Cache) -> String {
        self.loc.error(
            cache,
            if self.expecteds.len() == 1 {
                format!("expected {}", self.expecteds.iter().next().unwrap())
            } else {
                let mut s = "expected one of ".to_string();
                let mut expecteds = self.expecteds.iter();
                write!(s, "{}", expecteds.next().unwrap()).unwrap();
                for x in expecteds {
                    write!(s, " | {}", x).unwrap()
                }
                s
            },
        )
    }
}

#[derive(Clone)]
pub struct LexErr {
    pub expected: LexThing,
    pub found: LexThing,
    pub loc: Loc,
}

impl LexErr {
    pub fn render(&self, cache: &mut Cache) -> String {
        self.loc.error(
            cache,
            match (self.expected, self.found) {
                (LexThing::Anon, f) => format!("unexpected {f}"),
                (e, LexThing::Anon) => format!("expected {e}"),
                (e, f) => format!("expected {e}, found {f}"),
            },
        )
    }
}

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

#[derive(Debug, Clone, Copy)]
pub enum LexThing {
    Char(char),
    Descr(&'static str),
    Eof,
    Anon,
}

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),
            LexThing::Eof => write!(f, "end-of-file"),
            LexThing::Anon => write!(f, "<see source>"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub enum Loc {
    File { file: FileId, offset: u32 },
    FileGeneral { file: FileId },
    Anon { offset: u32 },
    AnonGeneral,
}

impl Loc {
    pub fn new(file: Option<FileId>, offset: u32) -> Self {
        match file {
            Some(file) => Self::File { file, offset },
            None => Self::Anon { offset },
        }
    }

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

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

    pub fn contextualize<M: Display>(&self, cache: &mut Cache, msg: M) -> String {
        match self {
            Loc::File { file, offset } => {
                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);
                let marker = " ".repeat(column_i as usize);
                let path = path.to_string_lossy();
                format!("{path}:{line_i}:{column_i}: {msg}\n{gutter} |\n{column_i} | {line_s}\n{gutter} | {marker}^",)
            }
            Loc::FileGeneral { file } => {
                let path = cache.get_path(*file);
                format!("{}: {msg}", path.to_string_lossy())
            }
            Loc::Anon { offset } => {
                format!("offset={offset}: {msg}")
            }
            Loc::AnonGeneral => format!("{msg}"),
        }
    }
}

// Calculate what line and column index the byte `offset` is at in the given string.
// Also include a slice of that complete line.
fn line_and_column_at(mut s: &str, mut offset: u32) -> (u32, u32, &str) {
    let mut line = 0;
    loop {
        let (i, eof) = match s.find('\n') {
            None => (s.len(), true),
            Some(i) => (i, false),
        };
        if i as u32 >= offset {
            let cr = i > 0 && s.as_bytes()[i - 1] == b'\r';
            let line_s = &s[0..(if cr { i - 1 } else { i })];
            let column = offset;
            break (line, column, line_s);
        }
        if eof {
            panic!("ice: unexpected end of file")
        }
        line += 1;
        s = &s[i + 1..];
        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()
}

D src/eval.rs => src/eval.rs +0 -118
@@ 1,118 0,0 @@
use crate::prelude::*;
use abase::*;
use base::*;
use std::collections::HashMap;

pub fn eval_fun_at(cache: &mut Cache, def: &FunDef, args: &[Operand]) -> Result<Operand> {
    EvalDef::new(cache, &def.body, args).run()
}

pub fn eval_var(cache: &mut Cache, def: &GVarDef) -> Result<Operand> {
    EvalDef::new(cache, &def.body, &[]).run()
}

#[derive(Debug)]
struct EvalDef<'c, 'd, 'a> {
    cache: &'c mut Cache,
    body: &'d Body,
    args: &'a [Operand],
    vars: HashMap<StmRef, Operand>,
}

impl<'c, 'd, 'a> EvalDef<'c, 'd, 'a> {
    fn new(cache: &'c mut Cache, body: &'d Body, args: &'a [Operand]) -> Self {
        Self { cache, body, args, vars: HashMap::new() }
    }

    fn run(mut self) -> Result<Operand> {
        self.eval_block_ref(self.body.root)
    }

    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> {
        self.eval_stms(&block.stms)?;
        self.eval_div(&block.tail)
    }

    fn eval_stms(&mut self, stms: &[StmRef]) -> Result<()> {
        for &stm in stms {
            self.eval_stm_ref(stm)?;
        }
        Ok(())
    }

    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)?;
                self.vars.insert(lhs, val);
                Ok(())
            }
        }
    }

    fn eval_div(&mut self, div: &Flow) -> Result<Operand> {
        use base::Const::*;
        use Operand::*;
        match div {
            Flow::Produce(ration) => self.eval_op(ration),
            &Flow::Diverge(Diverge::If(pred, conseq, alt)) => match self.eval_operand(pred)? {
                Const(Bool(true)) => self.eval_block_ref(conseq),
                Const(Bool(false)) => self.eval_block_ref(alt),
                pe => panic!("ice: `if` expects const bool predicate, found {pe:?}"),
            },
        }
    }

    fn eval_op(&mut self, ration: &Operation) -> Result<Operand> {
        use base::Const::*;
        use Binop::*;
        use Operand::*;
        match ration {
            &Operation::Binop(op, x, y) => match (self.eval_operand(x)?, self.eval_operand(y)?) {
                (Const(x), Const(y)) => Ok(Const(match op {
                    Add => (x + y).unwrap(),
                    Sub => (x - y).unwrap(),
                    Mul => (x * y).unwrap(),
                    Quot => (x / y).unwrap(),
                    Rem => (x % y).unwrap(),
                    Lt => Bool(x < y),
                    Gt => Bool(x > y),
                    Leq => Bool(x <= y),
                    Geq => Bool(x >= y),
                    Eq => Bool(x == y),
                    Neq => Bool(x != y),
                })),
                (x_e, y_e) => panic!("ice: cannot apply binary operation {op:?} to {x_e:?} and {y_e:?}"),
            },
            Operation::Call(f, xs) => match self.eval_operand(*f)? {
                Global(fid) => {
                    let xs = xs.iter().map(|x| self.eval_operand(*x)).collect::<Result<Vec<_>>>()?;
                    self.cache.fetch_evaluated_at(fid, &xs)
                }
                fe => panic!("ice: applying non-function {fe:?} ({f:?})"),
            },
            &Operation::Wrap(rand) => self.eval_operand(rand),
        }
    }

    fn eval_operand(&mut self, mut rand: Operand) -> Result<Operand> {
        let max_loops = 64;
        for _ in 0..max_loops {
            match rand {
                Operand::Const(_) => return Ok(rand),
                Operand::Param(ParamRef(i)) => rand = self.args[i as usize],
                Operand::Var(x) => rand = self.vars[&x],
                Operand::Global(id) => match *self.cache.fetch_evaluated(id)? {
                    Operand::Global(id2) if id == id2 => return Ok(Operand::Global(id)),
                    rand2 => rand = rand2,
                },
            }
        }
        panic!("ice: eval_operand looped {max_loops} times. This is probably a compiler bug.")
    }
}

D src/fem.rs => src/fem.rs +0 -50
@@ 1,50 0,0 @@
//! Effem core IR

use crate::prelude::*;

#[derive(Debug, Clone, PartialEq)]
pub enum Type {
    Bool,
    Int { width: u8, signed: bool },
    ISize,
    NSize,
    F64,
    Fun(Vec<Type>, Box<Type>),
}

impl Type {
    pub fn to_parsed(&self) -> parse::Type {
        match *self {
            Type::Bool => parse::Type::Bool,
            Type::Int { width, signed } => parse::Type::Int { width, signed },
            Type::ISize => parse::Type::ISize,
            Type::NSize => parse::Type::NSize,
            Type::F64 => parse::Type::F64,
            Type::Fun(ref ps, ref r) =>
                parse::Type::Fun(ps.iter().map(|t| t.to_parsed()).collect(), Box::new(r.to_parsed())),
        }
    }
}

impl std::fmt::Display for Type {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.to_parsed().fmt(f)
    }
}

#[derive(Debug, Clone)]
pub struct Expr {
    pub typ: Type,
    pub kind: ExprKind,
}

#[derive(Debug, Clone)]
pub enum ExprKind {
    Bool(bool),
    Int(i128),
    F64(f64),
    Fun(Vec<(PubIdent, LocalId)>, Box<Expr>),
    App(Box<Expr>, Vec<Expr>),
    Var(ResName),
    If(Box<Expr>, Box<Expr>, Box<Expr>),
}

D src/lex.rs => src/lex.rs +0 -544
@@ 1,544 0,0 @@
use crate::prelude::*;
use std::borrow::Cow;
use std::path::Path;

// TODO: Compare using:
//       - just a deeply nested tree with boxes
//       - deeply nested tree with arena allocator
//       - flat tree

#[derive(Debug, Clone, PartialEq)]
pub enum Tok<'s> {
    Int(i128),
    Float(f64),
    Str(Cow<'s, str>),
    Ident(&'s str),
    DotIn(Box<Token<'s>>, Box<Token<'s>>),
    DotPost(Box<Token<'s>>),
    Keyword(&'s str),
    Octothorpe,
    Octothorped(Box<Token<'s>>),
    Backslashed(Box<Token<'s>>),
    Parens(Vec<Token<'s>>),
    Brackets(Vec<Token<'s>>),
    Braces(Vec<Token<'s>>),
}

#[derive(Debug, Clone, PartialEq)]
pub struct Token<'s> {
    pub offset: u32,
    pub tok: Tok<'s>,
}

pub fn lex(f: Option<FileId>, src: &str) -> LResult<Vec<Token>> {
    assert!(src.len() < u32::MAX as usize);
    let mut sc = Scanner::new(f, src);
    sc.terms()
}

type LResult<T> = std::result::Result<T, LexErr>;

struct Scanner<'s> {
    src: &'s str,
    i: usize,
    file: Option<FileId>,
}

impl<'s> Scanner<'s> {
    fn new(file: Option<FileId>, src: &'s str) -> Self {
        Self { src, i: 0, file }
    }

    fn loc(&self) -> Loc {
        Loc::new(self.file, self.i as u32)
    }

    fn maybe_term(&mut self) -> LResult<Option<Token<'s>>> {
        self.skip_unsemantic();
        self.maybe_term_nospace()
    }

    fn maybe_term_nospace(&mut self) -> LResult<Option<Token<'s>>> {
        Ok(if let Some(x) = self.maybe_factor()? {
            Some(if let Some('.') = self.peek() {
                self.consume();
                if let Some(y) = self.maybe_term_nospace()? {
                    Token { offset: x.offset, tok: Tok::DotIn(Box::new(x), Box::new(y)) }
                } else {
                    Token { offset: x.offset, tok: Tok::DotPost(Box::new(x)) }
                }
            } else {
                x
            })
        } else {
            None
        })
    }

    fn maybe_factor(&mut self) -> LResult<Option<Token<'s>>> {
        let offset = self.i as u32;
        let c = match self.peek() {
            Some(c) => c,
            None => return Ok(None),
        };
        match c {
            '(' => {
                self.consume();
                let tts = self.terms()?;
                self.consume_expect(')')?;
                Ok(Some(Token { offset, tok: Tok::Parens(tts) }))
            }
            '[' => {
                self.consume();
                let tts = self.terms()?;
                self.consume_expect(']')?;
                Ok(Some(Token { offset, tok: Tok::Brackets(tts) }))
            }
            '{' => {
                self.consume();
                let tts = self.terms()?;
                self.consume_expect('}')?;
                Ok(Some(Token { offset, tok: Tok::Braces(tts) }))
            }
            ')' | ']' | '}' => Ok(None),
            ' ' => Ok(None),
            '0'..='9' => self.num(false).map(Some),
            '"' => self.string().map(|s| Some(Token { tok: Tok::Str(s), offset })),
            ':' => {
                self.consume();
                let id = self.ident()?;
                Ok(Some(Token { offset, tok: Tok::Keyword(id) }))
            }
            '#' => {
                self.consume();
                Ok(Some(match self.maybe_term_nospace()? {
                    Some(t) => Token { offset, tok: Tok::Octothorped(Box::new(t)) },
                    None => Token { offset, tok: Tok::Octothorpe },
                }))
            }
            '\\' => {
                self.consume();
                match self.maybe_term_nospace()? {
                    Some(t) => Ok(Some(Token { offset, tok: Tok::Backslashed(Box::new(t)) })),
                    None => Err(LexErr {
                        expected: LexThing::Descr("'\\' followed immediately by any token tree"),
                        found: LexThing::Anon,
                        loc: Loc::new(self.file, offset),
                    }),
                }
            }
            '-' if matches!(self.peek2(), Some('0'..='9')) => {
                self.consume();
                self.num(true).map(Some)
            }
            _ if ident_char(c) => self.ident().map(|id| Some(Token { offset, tok: Tok::Ident(id) })),
            _ => Err(LexErr {
                expected: LexThing::Descr("any token tree"),
                found: LexThing::Char(c),
                loc: Loc::new(self.file, offset),
            }),
        }
    }

    fn terms(&mut self) -> LResult<Vec<Token<'s>>> {
        let mut ts = Vec::new();
        while let Some(t) = self.maybe_term()? {
            ts.push(t)
        }
        Ok(ts)
    }

    fn ident(&mut self) -> LResult<&'s str> {
        self.find_on_line1(LexThing::Descr("identifier"), |c| !ident_char(c))
    }

    fn find_on_line1(&mut self, expected: LexThing, mut pred: impl FnMut(char) -> bool) -> LResult<&'s str> {
        let i0 = self.i;
        match self.input().find(|c| c == '\n' || pred(c)) {
            None => {
                self.i = self.src.len();
                if self.i > i0 {
                    Ok(&self.src[i0..self.i])
                } else {
                    Err(LexErr { expected, found: LexThing::Eof, loc: self.loc() })
                }
            }
            Some(0) => Err(LexErr { expected, found: LexThing::Char(self.peek().unwrap()), loc: self.loc() }),
            Some(n) => {
                self.i += n;
                Ok(&self.src[i0..self.i])
            }
        }
    }

    fn string(&mut self) -> LResult<Cow<'s, str>> {
        self.consume_expect('"')?;
        let start = self.i;
        let mut escapes = vec![];
        let end = loop {
            match self.consume_some(LexThing::Descr("string literal closing quote '\"'"))? {
                '\\' => {
                    let esc_start = self.i - 1;
                    let c = self.escape_sequence()?;
                    let esc_end = self.i;
                    escapes.push((esc_start, esc_end, c))
                }
                '"' => break self.i - 1,
                _ => (),
            }
        };
        Ok(if escapes.is_empty() {
            Cow::Borrowed(&self.src[start..end])
        } else {
            let escape_max_len = 10; // e.g. '\u{FF0088}'
            let mut s = String::with_capacity((end - start).saturating_sub(escape_max_len * escapes.len()));
            let mut i = start;
            for (e0, e1, c) in escapes {
                s.push_str(&self.src[i..e0]);
                s.push(c);
                i = e1;
            }
            s.push_str(&self.src[i..end]);
            Cow::Owned(s)
        })
    }

    fn escape_sequence(&mut self) -> LResult<char> {
        let loc = self.loc();
        match self.consume_some(LexThing::Descr("escape sequence"))? {
            'n' => Ok('\n'),
            'r' => Ok('\r'),
            't' => Ok('\t'),
            '\\' => Ok('\\'),
            '0' => Ok('\0'),
            'x' => {
                let x1 = self.oct_digit()?;
                let x2 = self.hex_digit()?;
                Ok(((x1 << 4) + x2) as char)
            }
            'u' => {
                let loc = self.loc();
                let mut acc = 0u32;
                for i in (0..4).rev() {
                    let x = self.hex_digit()?;
                    acc += (x as u32) << (4 * i);
                }
                char::from_u32(acc).ok_or(LexErr {
                    expected: LexThing::Descr("valid unicode character code"),
                    found: LexThing::Anon,
                    loc,
                })
            }
            'U' => {
                let loc = self.loc();
                let mut acc = 0u32;
                for i in (0..6).rev() {
                    let x = self.hex_digit()?;
                    acc += (x as u32) << (4 * i);
                }
                char::from_u32(acc).ok_or(LexErr {
                    expected: LexThing::Descr("valid unicode character code"),
                    found: LexThing::Anon,
                    loc,
                })
            }
            c => Err(LexErr { expected: LexThing::Descr("escape sequence"), found: LexThing::Char(c), loc }),
        }
    }

    fn oct_digit(&mut self) -> LResult<u8> {
        self.consume_pred(LexThing::Descr("octal digit"), |c| c.is_ascii_octdigit())
            .map(|c| c.to_digit(0o10).unwrap() as u8)
    }

    fn hex_digit(&mut self) -> LResult<u8> {
        self.consume_pred(LexThing::Descr("hex digit"), |c| c.is_ascii_hexdigit())
            .map(|c| c.to_digit(0x10).unwrap() as u8)
    }

    fn num(&mut self, sign: bool) -> LResult<Token<'s>> {
        let i0 = self.i as u32;
        let mut i1 = i0;
        let mut dot = false;
        loop {
            match self.peek() {
                Some('.') if !dot => dot = true,
                Some('0'..='9' | '_') => (),
                _ => break,
            }
            self.consume();
            i1 += 1;
        }
        if dot {
            // TODO: Do this in an in-place and accumulatively, avoiding heap alloc.
            //       To read accurately if we do it ourselves, see
            //       Clinger, "How to read floating point numbers accurately"
            //       https://dl.acm.org/doi/10.1145/93542.93557.
            let i0 = i0 - sign as u32;
            let mut s = self.src[i0 as usize..i1 as usize].to_string();
            s.retain(|c| c != '_');
            s.parse::<f64>().map(|x| Token { tok: Tok::Float(x), offset: i0 }).map_err(|_| LexErr {
                expected: LexThing::Descr("float literal that's not way too damn big or whatever's going on here"),
                found: LexThing::Anon,
                loc: Loc::new(self.file, i0),
            })
        } else {
            let x = self.src[i0 as usize..i1 as usize]
                .bytes()
                .filter(|&c| c != b'_')
                .fold(0u128, |x, c| x.saturating_mul(10).saturating_add((c - b'0') as u128));
            if (sign && x > i128::MIN as u128) || (!sign && x > i128::MAX as u128) {
                Err(LexErr {
                    expected: LexThing::Descr("integer literal within the range of a 128-bit signed integer [-170141183460469231731687303715884105728, 170141183460469231731687303715884105727]"),
                    found: LexThing::Descr("integer literal with a greater magnitude than that"),
                    loc: Loc::new(self.file, i0),
                })
            } else {
                Ok(Token { tok: Tok::Int(if sign { (x as i128).overflowing_neg().0 } else { x as i128 }), offset: i0 })
            }
        }
    }

    fn skip_unsemantic(&mut self) {
        let mut in_comment = false;
        while let Some(c) = self.peek() {
            if in_comment {
                if c == '\n' {
                    in_comment = false
                }
            } else if c == ';' {
                in_comment = true
            } else if !c.is_whitespace() {
                return;
            }
            self.consume();
        }
    }

    fn consume_pred(&mut self, expected: LexThing, pred: impl FnOnce(char) -> bool) -> LResult<char> {
        match self.peek() {
            Some(found) if pred(found) => {
                self.consume();
                Ok(found)
            }
            Some(found) => Err(LexErr { expected, found: LexThing::Char(found), loc: self.loc() }),
            None => Err(LexErr { expected, found: LexThing::Eof, loc: self.loc() }),
        }
    }

    fn consume_expect(&mut self, expected: char) -> LResult<()> {
        match self.peek() {
            Some(found) if expected == found => {
                self.consume();
                Ok(())
            }
            Some(found) =>
                Err(LexErr { expected: LexThing::Char(expected), found: LexThing::Char(found), loc: self.loc() }),
            None => Err(LexErr { expected: LexThing::Char(expected), found: LexThing::Eof, loc: self.loc() }),
        }
    }

    fn peek(&self) -> Option<char> {
        self.input().chars().next()
    }

    fn peek2(&self) -> Option<char> {
        self.input().chars().nth(1)
    }

    fn consume_some(&mut self, thing: LexThing) -> LResult<char> {
        self.consume().ok_or_else(|| LexErr { expected: thing, found: LexThing::Eof, loc: self.loc() })
    }

    fn consume(&mut self) -> Option<char> {
        self.input().chars().next().map(|c| {
            let n = c.len_utf8();
            self.i += n;
            c
        })
    }

    fn input(&self) -> &'s str {
        &self.src[self.i..]
    }
}

fn ident_char(c: char) -> bool {
    !(c.is_whitespace()
        || matches!(c, ',' | '.' | '`' | '\'' | '"' | '{' | '}' | '(' | ')' | '[' | ']' | '\\' | '#')
        || c.is_control())
}

#[cfg(test)]
mod test {
    use super::*;
    use std::assert_matches::*;

    #[test]
    fn test_int() {
        assert!(matches!(Scanner::new(None, "123_456").num(false).unwrap(), Token { tok: Tok::Int(123456), .. }))
    }

    #[test]
    fn test_float() {
        assert!(
            matches!(Scanner::new(None, "1_2.03_4").num(false).unwrap(), Token { tok: Tok::Float(x), .. } if x == 12.034)
        )
    }

    #[test]
    fn test_neg_float() {
        assert!(
            matches!(Scanner::new(None, "-1_2._03__4").maybe_term().unwrap().unwrap(), Token { tok: Tok::Float(x), .. } if x == -12.034)
        )
    }

    #[test]
    fn test_string() {
        assert_eq!(Scanner::new(None, r#""foo bar\nbaz\U01F61C""#).string().unwrap(), "foo bar\nbaz😜");
        assert_eq!(Scanner::new(None, r#""\u30B9\u30B7""#).string().unwrap(), "スシ")
    }

    #[test]
    fn test_ident() {
        assert_eq!(Scanner::new(None, "foo").ident().unwrap(), "foo");
        assert_eq!(Scanner::new(None, "-bar").ident().unwrap(), "-bar");
        assert_eq!(Scanner::new(None, "-").ident().unwrap(), "-");
        assert_eq!(Scanner::new(None, "*").ident().unwrap(), "*");
    }

    #[test]
    fn test_keyword() {
        let mut sc = Scanner::new(None, ":foo");
        let tok = sc.maybe_term().unwrap().unwrap();
        assert!(matches!(
            tok,
            Token { tok: Tok::Keyword(s), .. } if s == "foo"
        ))
    }

    #[test]
    fn test_space() {
        let mut sc = Scanner::new(None, "  \t \n\n123");
        let tok = sc.maybe_term().unwrap().unwrap();
        assert!(matches!(tok, Token { tok: Tok::Int(123), .. }))
    }

    #[test]
    fn test_comment() {
        let mut sc = Scanner::new(None, ";; yo\n  ;; hehe  \r\n 123");
        let tok = sc.maybe_term().unwrap().unwrap();
        assert!(matches!(tok, Token { tok: Tok::Int(123), .. }))
    }

    #[test]
    fn test_parens() {
        let mut sc = Scanner::new(None, "(+ 1 x)");
        let tok = sc.maybe_term().unwrap().unwrap();
        let tts = match tok {
            Token { tok: Tok::Parens(ref tts), .. } => tts,
            ref t => panic!("expected Tok::Parens, but result was {t:?}"),
        };
        assert_eq!(tts[0].tok, Tok::Ident("+"));
        assert_eq!(tts[1].tok, Tok::Int(1));
        assert_eq!(tts[2].tok, Tok::Ident("x"));
    }

    #[test]
    fn test_arithm() {
        let mut sc = Scanner::new(None, "(+ 1 2)");
        let tok = sc.maybe_term().unwrap().unwrap();
        let tts = match tok {
            Token { tok: Tok::Parens(ref tts), .. } => tts,
            ref t => panic!("expected Tok::Parens, but result was {t:?}"),
        };
        assert_eq!(tts[0].tok, Tok::Ident("+"));
        assert_eq!(tts[1].tok, Tok::Int(1));
        assert_eq!(tts[2].tok, Tok::Int(2));
    }

    #[test]
    fn test_def_arithm() {
        let ts = lex(None, "(def x (+ 1 2))").unwrap();
        assert_matches!(
            &ts[0],
            Token { tok: Tok::Parens(ts1), ..} if matches!(
                ts1.as_slice(),
                [Token{tok:Tok::Ident("def"), ..}, Token{tok: Tok::Ident("x"), ..}, Token{tok: Tok::Parens(ts2), ..}] if matches!(
                    ts2.as_slice(),
                    [Token{tok: Tok::Ident("+"), ..}, Token{ tok: Tok::Int(1), ..}, Token{ tok: Tok::Int(2), ..}]
                )
            )
        )
    }

    #[test]
    fn test_own() {
        let src = "own. ";
        let mut sc = Scanner::new(None, src);
        let t = sc.maybe_term().unwrap().unwrap().tok;
        match t {
            Tok::DotPost(c) if matches!(c.tok, Tok::Ident("own")) => (),
            _ => panic!("Lexd source {src:?} didn't match the expected pattern. Got {t:?}"),
        }
    }

    #[test]
    fn test_full_qual_name() {
        let src = "str.std.dep.";
        let mut sc = Scanner::new(None, src);
        let t = sc.maybe_term().unwrap().unwrap().tok;
        let matches = match t.clone() {
            Tok::DotIn(a, b) => match (a.tok, b.tok) {
                (Tok::Ident("str"), Tok::DotIn(b, c)) => match (b.tok, c.tok) {
                    (Tok::Ident("std"), Tok::DotPost(c)) => matches!(c.tok, Tok::Ident("dep")),
                    _ => false,
                },
                _ => false,
            },
            _ => false,
        };
        if !matches {
            panic!("Lexd source {src:?} didn't match the expected pattern. Got {t:?}")
        }
    }

    #[test]
    fn test_part_qual_name() {
        let mut sc = Scanner::new(None, "new.String");
        let t = sc.maybe_term().unwrap().unwrap().tok;
        assert!(match t {
            Tok::DotIn(a, b) => matches!((a.tok, b.tok), (Tok::Ident("new"), Tok::Ident("String"))),
            _ => false,
        })
    }

    #[test]
    fn test_octo() {
        let mut sc = Scanner::new(None, "#");
        let t = sc.maybe_term().unwrap().unwrap().tok;
        assert_eq!(t, Tok::Octothorpe)
    }

    #[test]
    fn test_octod() {
        let mut sc = Scanner::new(None, "#42");
        let t = sc.maybe_term().unwrap().unwrap().tok;
        assert_matches!(t, Tok::Octothorped(t) if t.tok == Tok::Int(42))
    }

    #[test]
    fn test_backslashed() {
        let mut sc = Scanner::new(None, "\\(+ #1 #2)");
        match sc.maybe_term().unwrap().unwrap().tok {
            Tok::Backslashed(a) => match a.tok {
                Tok::Parens(bs) => match &bs[2].tok {
                    Tok::Octothorped(e) => assert_eq!(e.tok, Tok::Int(2)),
                    e => panic!("{e:?} is not Octothorped"),
                },
                b => panic!("{b:?} is not Parens"),
            },
            a => panic!("{a:?} is not Backslashed"),
        }
    }
}

D src/main.rs => src/main.rs +0 -278
@@ 1,278 0,0 @@
//! The Effem Programming Language

#![feature(entry_insert, is_ascii_octdigit, trait_alias, assert_matches)]
#![allow(unused_imports)]
#![allow(clippy::map_entry)]

mod abase;
mod base;
mod cache;
mod check;
mod desugar;
mod diag;
mod eval;
mod fem;
mod lex;
mod name;
mod parse;
mod prelude;
mod resolve;

use prelude::*;
use std::borrow::Cow;
use std::path::Path;

fn main() {
    let main_path = std::env::args()
        .nth(1)
        // .expect("first argument should be entrypoint file (e.g. \"src/main.fm\")");
        .unwrap_or("/home/jojo/Hack/effem/tests/main.fm".to_owned());
    println!("   Running {main_path}");
    let (dir, module_name) = parse_source_file_path(Path::new(&main_path));
    let mut cache = Cache::for_package_in(dir);
    let main_name = name_in_cli(&["own", module_name, "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}")
    }
}

fn name_in_cli(segs: &[&str]) -> FullName<PubIdent> {
    let cli_loc = diag::Loc::AnonGeneral;
    FullName::new(segs.iter().map(|s| PubIdent { loc: cli_loc, s: s.to_string() }).collect())
}

fn with_pretty_errors<A>(
    cache: &mut Cache,
    f: impl FnOnce(&mut Cache) -> diag::Result<A>,
) -> std::result::Result<A, String> {
    match f(cache) {
        Ok(a) => Ok(a),
        Err(e) => Err(e.render(cache)),
    }
}

fn parse_source_file_path(p: &Path) -> (&Path, &str) {
    match p.extension() {
        Some(os) if os == "fm" => (),
        ext => panic!("expected extension .fm, found {ext:?}"),
    }
    let d = p.parent().unwrap_or(Path::new(""));
    if let Some(s) = p.file_stem() {
        if let Some(s) = s.to_str() {
            return (d, s);
        }
    }
    panic!("invalid module path {p:?}")
}

#[cfg(test)]
mod test {
    use super::*;
    use abase::*;
    use base::*;
    use diag::Result;
    use eval::*;
    use std::assert_matches::assert_matches;

    fn run_tmp(main_src: &str) -> Result<Operand> {
        let dir = tempfile::tempdir().unwrap();
        std::fs::write(dir.path().join("main.fm"), main_src).unwrap();
        let mut cache = Cache::for_package_in(dir.path());
        let r = (|cache: &mut Cache| {
            let main_id = cache.fetch_global_resolved_name(&name_in_cli(&["own", "main", "main"]))?.as_def().unwrap();
            cache.fetch_evaluated(main_id).cloned()
        })(&mut cache);
        if let Err(e) = &r {
            eprintln!("{}", e.render(&mut cache));
        }
        r
    }

    fn run_tmp_multi(app_module: &[&str], files: &[(&str, &str)]) -> Result<Operand> {
        let dir = tempfile::tempdir().unwrap();
        for (path, src) in files {
            std::fs::write(dir.path().join(Path::new(path)), src).unwrap();
        }
        let mut cache = Cache::for_package_in(dir.path());
        let r = (|cache: &mut Cache| {
            let main_id =
                cache.fetch_global_resolved_name(&name_in_cli(&[app_module, &["main"]].concat()))?.as_def().unwrap();
            cache.fetch_evaluated(main_id).cloned()
        })(&mut cache);
        if let Err(e) = &r {
            eprintln!("{}", e.render(&mut cache));
        }
        r
    }

    #[test]
    fn test_main_literal() {
        assert_matches!(run_tmp("(def main 123.456)"), Ok(Operand::Const(Const::F64(x))) if x == 123.456)
    }

    #[test]
    fn test_main_arithm1() {
        assert_matches!(run_tmp("(def main (+.prim. 1.0 2.0))"), Ok(Operand::Const(Const::F64(x))) if x == 3.0)
    }

    #[test]
    fn test_main_arithm2() {
        assert_matches!(run_tmp("(def main (+.prim. (*.prim. 13.0 100.0) 37.0))"), Ok(Operand::Const(Const::F64(x))) if x == 1337.0)
    }

    #[test]
    fn test_main_arithm_var() {
        let src = "
            (def x 1300.0)
            (def main (+ x 37.0))
        ";
        assert_matches!(run_tmp(src), Ok(Operand::Const(Const::F64(x))) if x == 1337.0)
    }

    #[test]
    fn test_app_lambda() {
        let src = "
            (def main (double 21.0))
            (def double (of (Fun [F64] F64)
                            (fun [x] (* x 2.0))))
        ";
        assert_matches!(run_tmp(src), Ok(Operand::Const(Const::F64(x))) if x == 42.0)
    }

    #[test]
    fn test_type_err_lambda_f64() {
        let src = "
            (def main (+ 1.0 (fun [x] (* x 2.0))))
        ";
        assert!(run_tmp(src).is_err())
    }

    #[test]
    fn test_modules1() {
        let main_src = "(def main (double.double.own. 21.0))";
        let double_src = "(def double (of (Fun [F64] F64) (fun [x] (* x 2.0))))";
        assert_matches!(run_tmp_multi(&["own", "main"], &[
            ("main.fm", main_src),
            ("double.fm", double_src)
        ]), Ok(Operand::Const(Const::F64(x))) if x == 42.0)
    }

    #[test]
    fn test_modules2() {
        let main_src = "(def main (double.own. 21.0))";
        let lib_src = "(def double (of (Fun [F64] F64) (fun [x] (* x 2.0))))";
        assert_matches!(run_tmp_multi(&["own", "main"], &[
            ("main.fm", main_src),
            ("lib.fm", lib_src)
        ]), Ok(Operand::Const(Const::F64(x))) if x == 42.0)
    }

    #[test]
    fn test_add_ints() {
        let src = "
            (def main (+ 40 2))
        ";
        assert_matches!(run_tmp(src), Ok(Operand::Const(Const::Int(42))))
    }

    #[test]
    fn test_add_neg_ints() {
        let src = "
            (def main (+ -40 -2))
        ";
        assert_matches!(run_tmp(src), Ok(Operand::Const(Const::Int(-42))))
    }

    #[test]
    fn test_check_i8() {
        let src = "
            (def main (of I8 42))
        ";
        assert_matches!(run_tmp(src), Ok(Operand::Const(Const::I8(42))))
    }

    #[test]
    fn test_type_err_add_i8_int() {
        let src = "
            (def main (+ (of I8 40) 2))
        ";
        assert!(run_tmp(src).is_err())
    }

    #[test]
    fn test_if() {
        let src = "
            (def abs (of (Fun [Int] Int) (fun [x] (if (< x 0) (- 0 x) x))))
            (def main (+ (abs 100) (abs -23)))
        ";
        assert_matches!(run_tmp(src), Ok(Operand::Const(Const::Int(123))))
    }

    #[test]
    fn test_add_i8() {
        let src = "
            (def main ((of (Fun [I8 _] _) +) 100 23)))
        ";
        assert_matches!(run_tmp(src), Ok(Operand::Const(Const::I8(123))))
    }

    #[test]
    fn test_i8_out_of_range() {
        let src = "
            (def main (of I8 200)))
        ";
        assert_matches!(run_tmp(src), Err(_))
    }

    #[test]
    fn test_dup_def() {
        let src = "
            (def main 123)
            (def main 456)
        ";
        assert_matches!(run_tmp(src), Err(_))
    }

    #[test]
    fn test_factorial() {
        let src = "
            (def main (factorial 10))
            (def factorial (of (Fun [Int] Int)
                               (fun [n] (if (= n 0) 1 (* n (factorial (- n 1)))))))
        ";
        assert_matches!(run_tmp(src), Ok(Operand::Const(Const::Int(3628800))))
    }

    #[test]
    fn test_mut_rec() {
        let src = "
            (def main (even? 20))
            (def even? (of (Fun [Int] Bool)
                           (fun [n] (if (= n 0) True (odd? (- n 1))))))
            (def odd? (of (Fun [Int] _)
                          (fun [n] (if (= n 1) True (even? (- n 1))))))
        ";
        assert_matches!(run_tmp(src), Ok(Operand::Const(Const::Bool(true))))
    }

    #[test]
    fn test_twice_mono() {
        let src = "
            (def twice (of (Fun [(Fun [Int] Int) Int] Int)
                           (fun [f x] (f (f x)))))
            (def main (twice (fun [x] (+ x 1)) 8))
        ";
        assert_matches!(run_tmp(src), Ok(Operand::Const(Const::Int(10))))
    }
}

D src/name.rs => src/name.rs +0 -241
@@ 1,241 0,0 @@
use crate::prelude::*;
use std::borrow::Cow;
use std::fmt;
use std::hash::Hash;
use std::path::Path;

#[derive(Debug, Clone)]
pub struct ParsedName {
    // TODO: This bool ends up wasting a whole word. Weave it into `segments` somehow & measure
    pub rooted: bool,
    pub segments: Vec<PrivIdent>,
}

impl ParsedName {
    pub fn unqualified(x: PrivIdent) -> Self {
        Self { rooted: false, segments: vec![x] }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct ResName {
    pub res: Res,
    // pos
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Res {
    Def(DefId),
    Local(LocalId),
    Prim(Prim),
    Module(ModuleId),
}

impl Res {
    pub fn as_def(&self) -> Option<DefId> {
        match *self {
            Res::Def(id) => Some(id),
            _ => None,
        }
    }
}

pub type DefId = u32;

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

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ModuleId(pub u32);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct FileId(pub u32);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Prim {
    Add,
    Sub,
    Mul,
    Quot,
    Rem,
    Lt,
    Gt,
    Leq,
    Geq,
    Eq,
    Neq,
}

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

/// A nonempty list representing a fully qualified name.
///
/// For example, ["pkg", "std", "str", "Str", "new"] represents the path "new.Str.str.std.pkg."
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FullName<S>(Vec<S>);

impl<S> FullName<S> {
    pub fn new(segments: Vec<S>) -> Self {
        assert!(segments.len() > 0);
        Self(segments)
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn init(mut self) -> Option<Self> {
        self.0.pop();
        if self.is_empty() {
            None
        } else {
            Some(self)
        }
    }

    pub fn rest(mut self) -> Option<Self> {
        self.0.remove(0);
        if self.is_empty() {
            None
        } else {
            Some(self)
        }
    }

    pub fn first(&self) -> &S {
        &self.0[0]
    }

    pub fn last(&self) -> &S {
        &self.0[self.0.len() - 1]
    }

    pub fn split_first(mut self) -> (S, Option<Self>) {
        (self.0.remove(0), (!self.0.is_empty()).then_some(self))
    }

    pub fn split_last(mut self) -> (S, Option<Self>) {
        (self.0.pop().unwrap(), (!self.0.is_empty()).then_some(self))
    }

    pub fn get(&self, i: usize) -> Option<&S> {
        self.0.get(i)
    }

    pub fn with(mut self, element: S) -> Self {
        self.0.push(element);
        self
    }

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

impl FullName<PubIdent> {
    pub fn loc(&self) -> Loc {
        self.last().loc
    }

    pub fn strip_locs(self) -> FullName<String> {
        FullName(self.0.into_iter().map(|PubIdent { s, .. }| s).collect())
    }
}

impl<S: fmt::Display> fmt::Display for FullName<S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for seg in self.0.iter().rev() {
            write!(f, "{}.", seg)?
        }
        Ok(())
    }
}

// The span of an identifier in the original source code string where the ident occured. Bookkeeping of source string
// must be handled manually.
//
// Wrt the naming, it's "private" like private vs. public IP addresses: a private IP (e.g. 192.168.0.1) is not globally
// routable, and the same private IP can show up in different networks, referring to different hosts depending on the
// network.
#[derive(Debug, Clone)]
pub struct PrivIdent {
    pub start: u32,
    pub len: u32, // TODO: Measure if leaving the length out and instead relying on re-tokenizing the ident is better.
}

impl PrivIdent {
    pub fn substr_in<'s>(&self, src: &'s str) -> &'s str {
        let i = self.start as usize;
        &src[i..i + self.len as usize]
    }

    pub fn to_pub<'s>(&self, cache: &mut Cache, file: FileId) -> Result<PubIdent> {
        Ok(self.substr_to_pub(cache.fetch_source(file)?, file))
    }

    pub fn substr_to_pub<'s>(&self, src: &str, file: FileId) -> PubIdent {
        let loc = Loc::File { file, offset: self.start };
        let s = self.substr_in(src).to_string();
        PubIdent { loc, s }
    }
}

#[derive(Debug, Clone)]
pub struct PubIdent {
    pub loc: Loc,
    pub s: String,
}

impl PubIdent {
    pub fn as_str(&self) -> &str {
        &self.s
    }
}

impl fmt::Display for PubIdent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.s, f)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    // #[test]
    // fn test_parse_source_file_path() {
    //     assert_eq!(parse_source_file_path(Path::new("/foo.fm")).unwrap(), (Path::new("/"), SIdent("foo")));
    //     assert_eq!(
    //         parse_source_file_path(Path::new("proj/My Game/main.fm")).unwrap(),
    //         (Path::new("proj/My Game"), SIdent("main"))
    //     );
    //     assert_eq!(parse_source_file_path(Path::new("bingo.new.fm")).unwrap(), (Path::new(""), SIdent("bingo.new")));
    //     assert!(parse_source_file_path(Path::new("bad")).is_err())
    // }
}

D src/parse.rs => src/parse.rs +0 -517
@@ 1,517 0,0 @@
use crate::prelude::*;
use lex::*;
use std::collections::{HashMap, HashSet};
use std::iter::once;
use std::path::Path;

#[derive(Debug)]
pub struct Module {
    pub defs: Vec<(PrivIdent, Expr)>,
}

// TODO: Parse directly to indexed flat tree.
//       Resolve doesn't create new string-based names, but simply associates the stringy representation of the name with an ID.
//       Globally unique IDs for top level function / constant definitions.
//       Other ID for local vars & function params.
//       See https://doc.rust-lang.org/stable/nightly-rustc/rustc_hir/def/enum.Res.html
//
//       Parse modules in parallel.
//       Global IDs generated from a shared atomic integer.
//       Definitions stored in a map per module, sparse keys.
//
//       Locals (expr nodes?) are stored in a contiguous array.
//       IDs are not globally unique. Instead, per top level item.
//       No syncing between threads necessary.
//       Different modules can't access eachothers locals.
#[derive(Debug, Clone)]
pub struct Expr {
    pub loc: Loc,
    pub kind: ExprKind,
}

#[derive(Debug, Clone)]
pub enum ExprKind {
    Bool(bool),
    Int(i128),
    F64(f64),
    Fun(Vec<PrivIdent>, Box<Expr>),
    App(Box<Expr>, Vec<Expr>),
    Var(ParsedName),
    Annot(Box<Expr>, Type),
    If(Box<Expr>, Box<Expr>, Box<Expr>),
}

#[derive(Debug, Clone, PartialEq)]
pub enum Type {
    Bool,
    Int { width: u8, signed: bool },
    ISize,
    NSize,
    F64,
    Fun(Vec<Type>, Box<Type>),
    Hole,
}

impl Type {
    pub fn to_fem(&self) -> Option<fem::Type> {
        Some(match *self {
            Type::Bool => fem::Type::Bool,
            Type::Int { width, signed } => fem::Type::Int { width, signed },
            Type::ISize => fem::Type::ISize,
            Type::NSize => fem::Type::NSize,
            Type::F64 => fem::Type::F64,
            Type::Fun(ref ps, ref r) =>
                fem::Type::Fun(ps.iter().map(|t| t.to_fem()).collect::<Option<_>>()?, Box::new(r.to_fem()?)),
            Type::Hole => return None,
        })
    }

    pub fn or<'a>(&'a self, other: &'a Self) -> &'a Self {
        if let Type::Hole = self {
            other
        } else {
            self
        }
    }
}

impl std::fmt::Display for Type {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Type::Bool => write!(f, "Bool"),
            Type::Int { width, signed: true } => write!(f, "I{width}"),
            Type::Int { width, signed: false } => write!(f, "N{width}"),
            Type::ISize => write!(f, "Int"),
            Type::NSize => write!(f, "Nat"),
            Type::F64 => write!(f, "F64"),
            Type::Fun(ps, r) => {
                write!(f, "(Fun [")?;
                if !ps.is_empty() {
                    write!(f, "{}", ps[0])?;
                }
                for p in &ps[1..] {
                    write!(f, " {p}")?;
                }
                write!(f, "] {r})")
            }
            Type::Hole => write!(f, "_"),
        }
    }
}

pub fn parse<'s>(file: Option<FileId>, tokens: &[Token<'s>]) -> std::result::Result<Module, ParseErr> {
    let inp = Inp { tokens, dist: 0, context: file.map(|file| Loc::FileGeneral { file }).unwrap_or(Loc::AnonGeneral) };
    let go = move || {
        let (inp, defs) = many0(def)(inp)?;
        end(inp)?;
        Ok(Module { defs })
    };
    go()
}

#[derive(Debug, Clone)]
struct Inp<'s, 't> {
    tokens: &'t [Token<'s>],
    dist: usize,
    context: Loc,
}

type Res<'s, 't, A> = std::result::Result<(Inp<'s, 't>, A), ParseErr>;

trait Parser<'s, 't, A> = FnMut(Inp<'s, 't>) -> Res<'s, 't, A> where 's: 't;

impl<'s, 't> Inp<'s, 't> {
    fn next(&self) -> Res<'s, 't, &'t Token<'s>> {
        match self.tokens.split_first() {
            Some((t, rest)) => Ok((Inp { tokens: rest, dist: self.dist + 1, context: self.context }, t)),
            None => Err(ParseErr {
                loc: self.context,
                dist: self.dist,
                expecteds: once("continuation of token sequence".to_string()).collect(),
            }),
        }
    }

    fn filter_next<A, S: std::fmt::Display>(
        &self,
        extract: impl FnOnce(&'t Token<'s>) -> std::result::Result<A, S>,
    ) -> Res<'s, 't, A> {
        let (inp, t) = self.next()?;
        match extract(t) {
            Ok(x) => Ok((inp, x)),
            Err(expected) => Err(ParseErr {
                loc: self.context.with_offset(t.offset),
                dist: self.dist,
                expecteds: once(expected.to_string()).collect(),
            }),
        }
    }

    fn enter<A>(
        &self,
        extract: impl FnOnce(&'t Tok<'s>) -> std::result::Result<&'t [Token<'s>], &'static str>,
        mut f: impl Parser<'s, 't, A>,
    ) -> Res<'s, 't, A> {
        let (rem, t) = self.next()?;
        match extract(&t.tok) {
            Ok(ts) => {
                let inp = Inp { tokens: ts, dist: rem.dist, context: self.context.with_offset(t.offset) };
                let (inp, x) = f(inp)?;
                let inp = Inp { tokens: rem.tokens, dist: inp.dist, context: rem.context };
                Ok((inp, x))
            }
            Err(expected) => Err(ParseErr {
                loc: self.context.with_offset(t.offset),
                dist: self.dist,
                expecteds: once(expected.to_string()).collect(),
            }),
        }
    }
}

fn def<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, (PrivIdent, Expr)> {
    parens(|inp| {
        let (inp, _) = special_form("def")(inp)?;
        let (inp, lhs) = ident(inp)?;
        let (inp, rhs) = expr(inp)?;
        let (inp, ()) = end(inp)?;
        Ok((inp, (lhs, rhs)))
    })(inp)
}

fn expr<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, Expr> {
    let loc = inp.context.with_offset(inp.next()?.1.offset);
    let (inp, kind) = alt5(
        map(lit_bool, ExprKind::Bool),
        map(lit_int, ExprKind::Int),
        map(lit_float, ExprKind::F64),
        map(name, ExprKind::Var),
        parens(pexpr),
    )(inp)?;
    Ok((inp, Expr { loc, kind }))
}

fn pexpr<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ExprKind> {
    alt4(
        |inp| {
            let (inp, _) = special_form("fun")(inp)?;
            let (inp, params) = brackets(rest(ident))(inp)?;
            let (inp, body) = expr(inp)?;
            let (inp, ()) = end(inp)?;
            Ok((inp, ExprKind::Fun(params, Box::new(body))))
        },
        |inp| {
            let (inp, _) = special_form("of")(inp)?;
            let (inp, (t, e)) = pair(typ, expr)(inp)?;
            Ok((inp, ExprKind::Annot(Box::new(e), t)))
        },
        |inp| {
            let (inp, _) = special_form("if")(inp)?;
            let (inp, pred) = expr(inp)?;
            let (inp, (conseq, alt)) = pair(expr, expr)(inp)?;
            Ok((inp, ExprKind::If(Box::new(pred), Box::new(conseq), Box::new(alt))))
        },
        map(pair(expr, rest(expr)), |(f, xs)| ExprKind::App(Box::new(f), xs)),
    )(inp)
}

fn typ<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, Type> {
    alt5(
        alt4(
            give(literally("I8"), Type::Int { width: 8, signed: true }),
            give(literally("I16"), Type::Int { width: 16, signed: true }),
            give(literally("I32"), Type::Int { width: 32, signed: true }),
            give(literally("I64"), Type::Int { width: 64, signed: true }),
        ),
        alt4(
            give(literally("N8"), Type::Int { width: 8, signed: false }),
            give(literally("N16"), Type::Int { width: 16, signed: false }),
            give(literally("N32"), Type::Int { width: 32, signed: false }),
            give(literally("N64"), Type::Int { width: 64, signed: false }),
        ),
        alt2(give(literally("Int"), Type::ISize), give(literally("Nat"), Type::NSize)),
        alt3(give(literally("F64"), Type::F64), give(literally("Bool"), Type::Bool), give(literally("_"), Type::Hole)),
        parens(ptyp),
    )(inp)
}

fn ptyp<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, Type> {
    let (inp, _) = special_form("Fun")(inp)?;
    let (inp, params) = brackets(rest(typ))(inp)?;
    let (inp, ret) = typ(inp)?;
    let (inp, ()) = end(inp)?;
    Ok((inp, Type::Fun(params, Box::new(ret))))
}

fn special_form<'s: 't, 't>(id: &'static str) -> impl Parser<'s, 't, PrivIdent> {
    literally(id)
}

fn literally<'s: 't, 't>(id: &'static str) -> impl Parser<'s, 't, PrivIdent> {
    move |inp| {
        inp.filter_next(|t| match t.tok {
            Tok::Ident(x) if x == id => Ok(PrivIdent { start: t.offset, len: x.len() as u32 }),
            _ => Err(id),
        })
    }
}

fn name<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ParsedName> {
    inp.filter_next(name_)
}

fn name_(t: &Token) -> std::result::Result<ParsedName, &'static str> {
    match t.tok {
        Tok::Ident(x) => Ok(ParsedName::unqualified(PrivIdent { start: t.offset, len: x.len() as u32 })),
        Tok::DotIn(ref first, ref rest) => match first.tok {
            Tok::Ident(x) => {
                let mut n = name_(rest)?;
                n.segments.push(PrivIdent { start: t.offset, len: x.len() as u32 });
                Ok(n)
            }
            _ => Err("*ident*"),
        },
        Tok::DotPost(ref last) => match last.tok {
            Tok::Ident(x) =>
                Ok(ParsedName { rooted: true, segments: vec![PrivIdent { start: t.offset, len: x.len() as u32 }] }),
            _ => Err("*ident*"),
        },
        _ => Err("*name*"),
    }
}

fn ident<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, PrivIdent> {
    inp.filter_next(|t| match t.tok {
        Tok::Ident(x) => Ok(PrivIdent { start: t.offset, len: x.len() as u32 }),
        _ => Err("*identifier*"),
    })
}

fn lit_float<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, f64> {
    inp.filter_next(|t| match t.tok {
        Tok::Float(x) => Ok(x),
        _ => Err("*float literal*"),
    })
}

fn lit_int<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, i128> {
    inp.filter_next(|t| match t.tok {
        Tok::Int(x) => Ok(x),
        _ => Err("*integer literal*"),
    })
}

fn lit_bool<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, bool> {
    inp.filter_next(|t| match t.tok {
        Tok::Ident("True") => Ok(true),
        Tok::Ident("False") => Ok(false),
        _ => Err("*boolean literal, True or False*"),
    })
}

// fn lit_int<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, i64> {
//     inp.filter_next(|tok| match *tok {
//         Tok::Int(x) => Ok(x),
//         _ => Err("*integer literal*"),
//     })
// }

fn parens<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>) -> impl Parser<'s, 't, A> {
    move |inp| {
        inp.enter(
            |tok| match tok {
                Tok::Parens(ts) => Ok(ts),
                _ => Err("(...)"),
            },
            |inp| {
                let (inp, x) = f(inp)?;
                let (inp, ()) = end(inp)?;
                Ok((inp, x))
            },
        )
    }
}

fn brackets<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>) -> impl Parser<'s, 't, A> {
    move |inp| {
        inp.enter(
            |tok| match tok {
                Tok::Brackets(ts) => Ok(ts),
                _ => Err("[...]"),
            },
            |inp| {
                let (inp, x) = f(inp)?;
                let (inp, ()) = end(inp)?;
                Ok((inp, x))
            },
        )
    }
}

fn end<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ()> {
    if let Some(t) = inp.tokens.first() {
        Err(ParseErr {
            loc: inp.context.with_offset(t.offset),
            dist: inp.dist,
            expecteds: once(format!("end of token sequence ({:?})", inp)).collect(),
        })
    } else {
        Ok((inp, ()))
    }
}

fn alt2<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>, mut g: impl Parser<'s, 't, A>) -> impl Parser<'s, 't, A> {
    move |inp| {
        let dist0 = inp.dist;
        let e1 = match f(inp.clone()) {
            y @ Ok(_) => return y,
            Err(e) if e.dist > dist0 => return Err(e),
            Err(e) => e,
        };
        let e2 = match g(inp.clone()) {
            y @ Ok(_) => return y,
            Err(e) if e.dist > dist0 => return Err(e),
            Err(e) => e,
        };
        let mut expecteds = e1.expecteds;
        expecteds.extend(e2.expecteds);
        Err(ParseErr { dist: dist0, loc: e1.loc, expecteds })
    }
}

fn alt3<'s: 't, 't, A>(
    mut f: impl Parser<'s, 't, A>,
    mut g: impl Parser<'s, 't, A>,
    mut h: impl Parser<'s, 't, A>,
) -> impl Parser<'s, 't, A> {
    move |inp| {
        let dist0 = inp.dist;
        let e1 = match f(inp.clone()) {
            y @ Ok(_) => return y,
            Err(e) if e.dist > dist0 => return Err(e),
            Err(e) => e,
        };
        let e2 = match g(inp.clone()) {
            y @ Ok(_) => return y,
            Err(e) if e.dist > dist0 => return Err(e),
            Err(e) => e,
        };
        let e3 = match h(inp.clone()) {
            y @ Ok(_) => return y,
            Err(e) if e.dist > dist0 => return Err(e),
            Err(e) => e,
        };
        Err(ParseErr {
            dist: dist0,
            loc: e1.loc,
            expecteds: [e1, e2, e3].into_iter().flat_map(|e| e.expecteds).collect(),
        })
    }
}

fn alt4<'s: 't, 't, A>(
    f: impl Parser<'s, 't, A>,
    g: impl Parser<'s, 't, A>,
    h: impl Parser<'s, 't, A>,
    i: impl Parser<'s, 't, A>,
) -> impl Parser<'s, 't, A> {
    alt2(alt2(f, g), alt2(h, i))
}

fn alt5<'s: 't, 't, A>(
    f: impl Parser<'s, 't, A>,
    g: impl Parser<'s, 't, A>,
    h: impl Parser<'s, 't, A>,
    i: impl Parser<'s, 't, A>,
    j: impl Parser<'s, 't, A>,
) -> impl Parser<'s, 't, A> {
    alt3(alt2(f, g), alt2(h, i), j)
}

fn many0<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>) -> impl Parser<'s, 't, Vec<A>> {
    move |mut inp| {
        let mut xs = vec![];
        loop {
            match f(inp.clone()) {
                Ok((rem, x)) => {
                    xs.push(x);
                    inp = rem
                }
                Err(e) if e.dist == inp.dist => return Ok((inp, xs)),
                Err(e) => return Err(e),
            }
        }
    }
}

fn rest<'s: 't, 't, A>(mut f: impl Parser<'s, 't, A>) -> impl Parser<'s, 't, Vec<A>> {
    move |mut inp| {
        let mut xs = vec![];
        while !inp.tokens.is_empty() {
            let (rem, x) = f(inp.clone())?;
            xs.push(x);
            inp = rem
        }
        Ok((inp, xs))
    }
}

fn pair<'s: 't, 't, A, B>(mut f: impl Parser<'s, 't, A>, mut g: impl Parser<'s, 't, B>) -> impl Parser<'s, 't, (A, B)> {
    move |inp| {
        let (inp, a) = f(inp)?;
        let (inp, b) = g(inp)?;
        Ok((inp, (a, b)))
    }
}

fn map<'s: 't, 't, A, B>(mut f: impl Parser<'s, 't, A>, mut g: impl FnMut(A) -> B) -> impl Parser<'s, 't, B> {
    move |inp| f(inp).map(|(inp, x)| (inp, g(x)))
}

fn give<'s: 't, 't, A, B: Clone>(mut f: impl Parser<'s, 't, A>, x: B) -> impl Parser<'s, 't, B> {
    move |inp| f(inp).map(|(inp, _)| (inp, x.clone()))
}

#[cfg(test)]
mod test {
    use super::*;
    use std::assert_matches::*;

    #[test]
    fn test_arithm() {
        let src = "(def x (+ 1.0 2.0))";
        let m = parse(None, &lex(None, src).unwrap()).unwrap();
        assert_matches!(
            &m.defs[0].1,
            Expr{kind:ExprKind::App(f, xs),..}
            if matches!(&**f, Expr{ kind: ExprKind::Var(name), ..} if name.segments.len() == 1 && matches!(name.segments[0], PrivIdent { start: 8, len: 1 }))
            && matches!(&xs[..], [Expr{kind:ExprKind::F64(x1),..}, Expr{kind:ExprKind::F64(x2),..}] if *x1 == 1.0 && *x2 == 2.0)
        )
    }

    #[test]
    fn test_lambda() {
        let src = "(def add (fun [x y] (+ x y)))";
        let m = parse(None, &lex(None, src).unwrap()).unwrap();
        assert_matches!(
            &m.defs[0].1,
            Expr{kind:ExprKind::Fun(ps, b),..}
            if matches!(ps[..], [PrivIdent { start: 15, len: 1 }, PrivIdent { start: 17, len: 1 }])
            && matches!(&**b, Expr{kind:ExprKind::App(f, xs),..}
                if matches!(&**f, Expr{kind:ExprKind::Var(f),..} if f.segments.len() == 1 && matches!(f.segments[0], PrivIdent { start: 21, len: 1 }))
                && matches!(&xs[..], [Expr{kind:ExprKind::Var(x1),..}, Expr{kind:ExprKind::Var(x2),..}] if x1.segments[0].start == 23 && x2.segments[0].start == 25))
        )
    }

    #[test]
    fn test_expr_annotation1() {
        let src = "(def foo (of F64 123.0))";
        let m = parse(None, &lex(None, src).unwrap()).unwrap();
        assert_matches!(
            &m.defs[0].1,
            Expr{kind:ExprKind::Annot(e, t),..}
            if matches!(&**e, Expr{kind:ExprKind::F64(x),..} if *x == 123.0)
            && matches!(t, Type::F64)
        )
    }
}

D src/prelude.rs => src/prelude.rs +0 -16
@@ 1,16 0,0 @@
pub(crate) use crate::{abase, base, cache, check, diag, eval, fem, lex, name, parse, resolve};
pub(crate) use cache::{Cache, Fetch};
pub(crate) use diag::*;
pub(crate) use name::*;

// pub fn mapl<A1, A2, B>((a, b): (A1, B), f: impl FnOnce(A1) -> A2) -> (A2, B) {
//     (f(a), b)
// }

pub fn mapr<A, B1, B2>((a, b): (A, B1), f: impl FnOnce(B1) -> B2) -> (A, B2) {
    (a, f(b))
}

// pub fn bimap<A1, A2, B1, B2>((a, b): (A1, B1), f: impl FnOnce(A1) -> A2, g: impl FnOnce(B1) -> B2) -> (A2, B2) {
//     (f(a), g(b))
// }

D src/resolve.rs => src/resolve.rs +0 -112
@@ 1,112 0,0 @@
use crate::prelude::*;
use diag::ResolveErr::*;
pub use parse::Type;
use std::borrow::Cow;
use std::collections::HashMap;

// https://rustc-dev-guide.rust-lang.org/name-resolution.html

// ett item vet sitt fulla namn
// vi resolvar alltid till item-referenser, inte namn. tänk på glob imports
// => resolve dekorerar givet namn med ett item id

#[derive(Debug, Clone)]
pub struct Expr {
    pub loc: Loc,
    pub kind: ExprKind,
}

#[derive(Debug, Clone)]
pub enum ExprKind {
    Bool(bool),
    Int(i128),
    F64(f64),
    Fun(Vec<(PubIdent, LocalId)>, Box<Expr>),
    App(Box<Expr>, Vec<Expr>),
    Var(ResName),
    Annot(Box<Expr>, Type),
    If(Box<Expr>, Box<Expr>, Box<Expr>),
}

pub fn resolve_def(cache: &mut Cache, parent_module: ModuleId, def_body: &parse::Expr) -> Result<Expr> {
    let mut resolver = Resolver::new(cache, parent_module)?;
    resolver.resolve(def_body)
}

struct Resolver<'c> {
    cache: &'c mut Cache,
    local_count: u32,
    scopes: Vec<HashMap<String, Res>>,
    module: ModuleId,
    file: FileId,
}

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.get_module_file(module);
        Ok(Self { cache, local_count: 0, scopes: vec![module_top_level], module, file })
    }

    fn resolve(&mut self, e: &parse::Expr) -> Result<Expr> {
        use parse::ExprKind as Pek;
        use ExprKind as Ek;
        let kind = match &e.kind {
            &Pek::Bool(x) => Ek::Bool(x),
            &Pek::Int(x) => Ek::Int(x),
            &Pek::F64(x) => Ek::F64(x),
            Pek::Fun(params, body) => {
                let rparams =
                    params.iter().map(|p| Ok((self.pub_ident(p)?, self.gen_local_id()))).collect::<Result<Vec<_>>>()?;
                self.scopes.push(rparams.iter().map(|(p, id)| (p.s.clone(), Res::Local(*id))).collect());
                let rbody = self.resolve(body)?;
                self.scopes.pop();
                Ek::Fun(rparams, Box::new(rbody))
            }
            Pek::App(f, args) =>
                Ek::App(Box::new(self.resolve(f)?), args.iter().map(|a| self.resolve(a)).collect::<Result<_>>()?),
            Pek::Var(v) => {
                // To begin with, our lang is very primitive, but there is still some symbol scoping to consider.
                // If main.fm defines foo and foo', and misc.fm defines bar, then
                // - `foo` can refer to `foo'` as just `foo'`, but must refer to `bar` by the full `bar.misc.own.`
                // - `bar` must refer to `foo` by the full `foo.main.own.`
                // So `resolve` must be aware of the module it's in from the start
                Ek::Var(ResName { res: self.resolve_parsed_name(v)? })
            }
            Pek::Annot(e, t) => Ek::Annot(Box::new(self.resolve(e)?), t.clone()),
            Pek::If(p, c, a) =>
                Ek::If(Box::new(self.resolve(p)?), Box::new(self.resolve(c)?), Box::new(self.resolve(a)?)),
        };
        Ok(Expr { loc: e.loc, kind })
    }

    fn resolve_parsed_name(&mut self, pname: &ParsedName) -> Result<Res> {
        debug_assert!(!pname.segments.is_empty());
        let src = self.cache.fetch_source(self.file)?;
        let name = FullName::new(pname.segments.iter().map(|seg| seg.substr_to_pub(src, self.file)).collect());
        if pname.rooted {
            self.cache.fetch_global_resolved_name(&name)
        } else {
            match name.split_first() {
                (first, None) => self
                    .scopes
                    .iter()
                    .rev()
                    .find_map(|scope| scope.get(first.as_str()))
                    .cloned()
                    .ok_or_else(|| Error::Resolve(UndefInMod(first.clone(), self.module))),
                (_first, _rest) => todo!(),
            }
        }
    }

    fn pub_ident(&mut self, priv_ident: &PrivIdent) -> Result<PubIdent> {
        priv_ident.to_pub(self.cache, self.file)
    }

    fn gen_local_id(&mut self) -> LocalId {
        assert!(self.local_count < u32::MAX);
        self.local_count += 1;
        LocalId(self.local_count - 1)
    }
}