~jojo/effem

841c40790ee811a2bed241a41fecb95e096b1948 — JoJo 3 months ago 7d0e00e
add integers
10 files changed, 237 insertions(+), 74 deletions(-)

M src/abase.rs
M src/cache.rs
M src/check.rs
M src/eval.rs
M src/fem.rs
M src/lex.rs
M src/main.rs
M src/name.rs
M src/parse.rs
M src/resolve.rs
M src/abase.rs => src/abase.rs +27 -3
@@ 1,7 1,7 @@
//! Base -- the lowest level IR in Effem before generating the output machine code.

use crate::prelude::*;
use fem::Expr as FExpr;
use fem::{Expr as FExpr, Type};

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


@@ 59,6 59,16 @@ pub enum Operand {

#[derive(Clone, Debug)]
pub enum Const {
    I8(i8),
    I16(i16),
    I32(i32),
    I64(i64),
    Int(isize),
    N8(u8),
    N16(u16),
    N32(u32),
    N64(u64),
    Nat(usize),
    F64(f64),
}



@@ 101,18 111,32 @@ impl<'c> AbaseDef<'c> {
    fn abase(&mut self, expr: &FExpr) -> Result<Expr> {
        use crate::fem::ExprKind::*;
        match &expr.kind {
            &Int(x) => Ok(Expr::Operand(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(Expr::Operand(Operand::Const(Const::F64(*x)))),
            Fun(ps, b) => Ok(Expr::Operand(Operand::Local(self.abase_fun(ps, b)?))),
            App(f, xs) => match &f.kind {
                Var(ResName { res: Res::Prim(p) }) => match p {
                    Prim::Add => {
                    Prim::Add | Prim::AddF => {
                        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(Expr::Add(x, y))
                    }
                    Prim::Mul => {
                    Prim::Mul | Prim::MulF => {
                        let x = self.abase(&xs[0])?;
                        let x = self.let_anon_soft(x)?;
                        let y = self.abase(&xs[1])?;

M src/cache.rs => src/cache.rs +8 -1
@@ 118,7 118,12 @@ impl Cache {
    }

    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::Mul))];
        static PRELUDE: &[(&str, Res)] = &[
            ("+", Res::Prim(Prim::Add)),
            ("*", Res::Prim(Prim::Mul)),
            ("+f", Res::Prim(Prim::AddF)),
            ("*f", Res::Prim(Prim::MulF)),
        ];

        if self.module_local_resolved_names.contains_key(&module_id) {
            Ok(&self.module_local_resolved_names[&module_id])


@@ 144,6 149,8 @@ impl Cache {
            match sname.get(1).unwrap().as_str() {
                "+" => Ok(Res::Prim(Prim::Add)),
                "*" => Ok(Res::Prim(Prim::Mul)),
                "+f" => Ok(Res::Prim(Prim::AddF)),
                "*f" => Ok(Res::Prim(Prim::MulF)),
                _ => todo!(), // Err
            }
        } else if let Some(&res) = self.resolved_names.get(&sname) {

M src/check.rs => src/check.rs +26 -0
@@ 25,6 25,16 @@ impl<'c> CheckDef<'c> {
    fn infer(&mut self, expr: &RExpr) -> Result<Expr> {
        use resolve::ExprKind as Rek;
        match &expr.kind {
            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)?;


@@ 53,6 63,10 @@ impl<'c> CheckDef<'c> {
                None => panic!("ice: undefined local var of id {id}"),
            },
            Rek::Var(r @ ResName { res: Res::Prim(Prim::Add | Prim::Mul) }) => 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::AddF | Prim::MulF) }) => Ok(Expr {
                typ: Type::Fun(vec![Type::F64, Type::F64], Box::new(Type::F64)),
                kind: ExprKind::Var(r.clone()),
            }),


@@ 64,6 78,18 @@ impl<'c> CheckDef<'c> {
    fn check(&mut self, expr: &RExpr, expected: &Type) -> Result<Expr> {
        use resolve::ExprKind as Rek;
        match &expr.kind {
            &Rek::Int(x) => {
                match expected {
                    Type::Int { width: 128, signed: true } if i128::MIN <= x && x <= i128::MAX => (),
                    Type::Int { width: 128, signed: false } if 0 <= x && x as u128 <= u128::MAX => (),
                    Type::Int { width, signed: true } if -(1 << (width - 1)) <= x && x < (1 << (width - 1)) => (),
                    Type::Int { width, signed: false } if 0 <= x && x < (1 << width) => (),
                    Type::ISize if isize::MIN as i128 <= x && x <= isize::MAX as i128 => (),
                    Type::NSize if 0 <= x && x as u128 <= usize::MAX as u128 => (),
                    _ => return err(ExpectedFound(expr.loc, expected.clone(), self.infer(expr)?.typ)),
                }
                Ok(Expr { typ: expected.clone(), kind: ExprKind::Int(x) })
            }
            Rek::Fun(ps, b) =>
                if let Type::Fun(tps, tr) = expected {
                    if ps.len() != tps.len() {

M src/eval.rs => src/eval.rs +2 -0
@@ 44,10 44,12 @@ impl<'c, 'd> EvalDef<'c, 'd> {
        match expr {
            Expr::Add(x, y) => match (self.eval_operand(x)?, self.eval_operand(y)?) {
                (Operand::Const(Const::F64(x)), Operand::Const(Const::F64(y))) => Ok(Operand::Const(Const::F64(x + y))),
                (Operand::Const(Const::Int(x)), Operand::Const(Const::Int(y))) => Ok(Operand::Const(Const::Int(x + y))),
                (xe, ye) => panic!("ice: cannot add {xe:?} + {ye:?} ({x:?} + {y:?})"),
            },
            Expr::Mul(x, y) => match (self.eval_operand(x)?, self.eval_operand(y)?) {
                (Operand::Const(Const::F64(x)), Operand::Const(Const::F64(y))) => Ok(Operand::Const(Const::F64(x * y))),
                (Operand::Const(Const::Int(x)), Operand::Const(Const::Int(y))) => Ok(Operand::Const(Const::Int(x * y))),
                (xe, ye) => panic!("ice: cannot add {xe:?} * {ye:?} ({x:?} * {y:?})"),
            },
            Expr::Call(f, xs) => match self.eval_operand(f)? {

M src/fem.rs => src/fem.rs +1 -0
@@ 11,6 11,7 @@ pub struct Expr {

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

M src/lex.rs => src/lex.rs +51 -32
@@ 9,7 9,7 @@ use std::path::Path;

#[derive(Debug, Clone, PartialEq)]
pub enum Tok<'s> {
    Int(i64),
    Int(i128),
    Float(f64),
    Str(Cow<'s, str>),
    Ident(&'s str),


@@ 102,7 102,7 @@ impl<'s> Scanner<'s> {
            }
            ')' | ']' | '}' => Ok(None),
            ' ' => Ok(None),
            '0'..='9' => self.num().map(Some),
            '0'..='9' => self.num(false).map(Some),
            '"' => self.string().map(|s| Some(Token { tok: Tok::Str(s), offset })),
            ':' => {
                self.consume();


@@ 127,11 127,10 @@ impl<'s> Scanner<'s> {
                    }),
                }
            }
            '-' if matches!(self.peek2(), Some('0'..='9')) => Ok(Some(match self.num()? {
                Token { offset, tok: Tok::Int(x) } => Token { offset, tok: Tok::Int(-x) },
                Token { offset, tok: Tok::Float(x) } => Token { offset, tok: Tok::Float(-x) },
                _ => unreachable!(),
            })),
            '-' 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"),


@@ 257,35 256,46 @@ impl<'s> Scanner<'s> {
            .map(|c| c.to_digit(0x10).unwrap() as u8)
    }

    fn num(&mut self) -> LResult<Token<'s>> {
        let offset = self.i as u32;
        let mut acc = 0u64;
    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('.') => break self.float(acc),
                Some(c @ '0'..='9') => acc = acc * 10 + (c as u8 - b'0') as u64,
                Some('_') => (),
                _ => break Ok(Token { tok: Tok::Int(acc as i64), offset }),
                Some('.') if !dot => dot = true,
                Some('0'..='9' | '_') => (),
                _ => break,
            }
            self.consume();
            i1 += 1;
        }
    }

    fn float(&mut self, left: u64) -> LResult<Token<'s>> {
        // TODO: Read floats accurately. See [(Clinger, "How to read floating point numbers accurately")](https://dl.acm.org/doi/10.1145/93542.93557).
        self.consume_expect('.')?;
        let mut factor = 0.1;
        let mut right = 0.0;
        loop {
            match self.peek() {
                Some(c @ '0'..='9') => {
                    right += factor * (c as u8 - b'0') as f64;
                    factor *= 0.1;
                }
                Some('_') => (),
                _ => break Ok(Token { tok: Tok::Float(left as f64 + right), offset: self.i as u32 }),
        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 })
            }
            self.consume();
        }
    }



@@ 366,12 376,21 @@ mod test {

    #[test]
    fn test_int() {
        assert!(matches!(Scanner::new(None, "123_456").num(), Ok(Token { tok: Tok::Int(123456), .. })))
        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(), Ok(Token { tok: Tok::Float(x), .. }) if x == 12.034))
        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]

M src/main.rs => src/main.rs +60 -24
@@ 75,47 75,51 @@ mod test {
    use diag::Result;
    use eval::*;

    fn run_tmp(main_src: &str) -> std::result::Result<Operand, String> {
    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());
        with_pretty_errors(&mut cache, |cache| {
            let main_id =
                cache.fetch_global_resolved_name(&name_in_cli(&["own", "main", "main"])).unwrap().as_def().unwrap();
        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)]) -> std::result::Result<Operand, String> {
    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());
        with_pretty_errors(&mut cache, |cache| {
            let main_id = cache
                .fetch_global_resolved_name(&name_in_cli(&[app_module, &["main"]].concat()))
                .unwrap()
                .as_def()
                .unwrap();
        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)").unwrap(), Operand::Const(Const::F64(x)) if x == 123.456))
        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))").unwrap(), Operand::Const(Const::F64(x)) if x == 3.0))
        assert!(matches!(run_tmp("(def main (+f.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))").unwrap(), Operand::Const(Const::F64(x)) if x == 1337.0)
            matches!(run_tmp("(def main (+f.prim. (*f.prim. 13.0 100.0) 37.0))"), Ok(Operand::Const(Const::F64(x))) if x == 1337.0)
        )
    }



@@ 123,9 127,9 @@ mod test {
    fn test_main_arithm_var() {
        let src = "
            (def x 1300.0)
            (def main (+ x 37.0))
            (def main (+f x 37.0))
        ";
        assert!(matches!(run_tmp(src).unwrap(), Operand::Const(Const::F64(x)) if x == 1337.0))
        assert!(matches!(run_tmp(src), Ok(Operand::Const(Const::F64(x))) if x == 1337.0))
    }

    #[test]


@@ 133,9 137,9 @@ mod test {
        let src = "
            (def main (double 21.0))
            (def double (of (Fun [F64] F64)
                            (fun [x] (* x 2.0))))
                            (fun [x] (*f x 2.0))))
        ";
        assert!(matches!(run_tmp(src).unwrap(), Operand::Const(Const::F64(x)) if x == 42.0))
        assert!(matches!(run_tmp(src), Ok(Operand::Const(Const::F64(x))) if x == 42.0))
    }

    #[test]


@@ 149,20 153,52 @@ mod test {
    #[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))))";
        let double_src = "(def double (of (Fun [F64] F64) (fun [x] (*f x 2.0))))";
        assert!(matches!(run_tmp_multi(&["own", "main"], &[
            ("main.fm", main_src),
            ("double.fm", double_src)
        ]).unwrap(), Operand::Const(Const::F64(x)) if x == 42.0))
        ]), 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))))";
        let lib_src = "(def double (of (Fun [F64] F64) (fun [x] (*f x 2.0))))";
        assert!(matches!(run_tmp_multi(&["own", "main"], &[
            ("main.fm", main_src),
            ("lib.fm", lib_src)
        ]).unwrap(), Operand::Const(Const::F64(x)) if x == 42.0))
        ]), 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())
    }
}

M src/name.rs => src/name.rs +6 -2
@@ 53,8 53,10 @@ pub struct FileId(pub u32);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Prim {
    Add = 0,
    Mul = 1,
    Add,
    Mul,
    AddF,
    MulF,
}

impl fmt::Display for Prim {


@@ 65,6 67,8 @@ impl fmt::Display for Prim {
            match self {
                Prim::Add => "+",
                Prim::Mul => "*",
                Prim::AddF => "+f",
                Prim::MulF => "*f",
            }
        )
    }

M src/parse.rs => src/parse.rs +54 -12
@@ 30,6 30,7 @@ pub struct Expr {

#[derive(Debug, Clone)]
pub enum ExprKind {
    Int(i128),
    F64(f64),
    Fun(Vec<PrivIdent>, Box<Expr>),
    App(Box<Expr>, Vec<Expr>),


@@ 39,6 40,9 @@ pub enum ExprKind {

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


@@ 46,6 50,10 @@ pub enum Type {
impl std::fmt::Display for Type {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            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 [")?;


@@ 82,9 90,9 @@ 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.clone() }, t)),
            Some((t, rest)) => Ok((Inp { tokens: rest, dist: self.dist + 1, context: self.context }, t)),
            None => Err(ParseErr {
                loc: self.context.clone(),
                loc: self.context,
                dist: self.dist,
                expecteds: once("continuation of token sequence".to_string()).collect(),
            }),


@@ 140,7 148,8 @@ fn def<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, (PrivIdent, Expr)> {

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) = alt3(map(lit_float, ExprKind::F64), map(name, ExprKind::Var), parens(pexpr))(inp)?;
    let (inp, kind) =
        alt4(map(lit_int, ExprKind::Int), map(lit_float, ExprKind::F64), map(name, ExprKind::Var), parens(pexpr))(inp)?;
    Ok((inp, Expr { loc, kind }))
}



@@ 163,7 172,23 @@ fn pexpr<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, ExprKind> {
}

fn typ<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, Type> {
    alt2(give(literally("F64"), Type::F64), parens(ptyp))(inp)
    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)),
        give(literally("F64"), Type::F64),
        parens(ptyp),
    )(inp)
}

fn ptyp<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, Type> {


@@ 225,6 250,13 @@ fn lit_float<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, f64> {
    })
}

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_int<'s: 't, 't>(inp: Inp<'s, 't>) -> Res<'s, 't, i64> {
//     inp.filter_next(|tok| match *tok {
//         Tok::Int(x) => Ok(x),


@@ 301,14 333,24 @@ fn alt3<'s: 't, 't, A>(
    alt2(f, alt2(g, h))
}

// 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 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| {

M src/resolve.rs => src/resolve.rs +2 -0
@@ 18,6 18,7 @@ pub struct Expr {

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


@@ 49,6 50,7 @@ impl<'c> Resolver<'c> {
        use parse::ExprKind as Pek;
        use ExprKind as Ek;
        let kind = match &e.kind {
            Pek::Int(x) => Ek::Int(*x),
            Pek::F64(x) => Ek::F64(*x),
            Pek::Fun(params, body) => {
                let rparams =