From fcac4db1dcdc73b4fe0bd4723d45aeb6f27c77c0 Mon Sep 17 00:00:00 2001 From: mehbark Date: Fri, 2 Jan 2026 01:55:25 -0500 Subject: [PATCH] parse functions --- src/parser.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/val.rs | 2 +- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 27cfa87..3860b3c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -44,6 +44,9 @@ enum Token<'a> { #[token("^")] Return, + #[token("fn")] + Fn, + #[regex(r"[+*/%=-]")] BinOp(&'a str), @@ -70,6 +73,7 @@ impl fmt::Display for Token<'_> { Token::OpenBrace => write!(f, "{{"), Token::CloseBrace => write!(f, "}}"), Token::Return => write!(f, "^"), + Token::Fn => write!(f, "fn"), Token::BinOp(x) | Token::Var(x) | Token::Num(x) => write!(f, "{x}"), Token::Error => write!(f, "ERROR"), } @@ -80,6 +84,7 @@ fn error_ast() -> Ast { Ast::Var(Ident::Local("!invalid".to_owned())) } +#[allow(clippy::too_many_lines, reason = "you're right but leave me alone")] fn parser<'tokens, 'src: 'tokens, I>() -> impl Parser<'tokens, I, Vec, extra::Err>>> where @@ -128,8 +133,8 @@ where let block = expr .clone() .separated_by(semicolon.clone()) + .allow_trailing() .collect() - .map(Ast::Block) .delimited_by(just(Token::OpenParen), just(Token::CloseParen)) .labelled("block"); @@ -142,12 +147,60 @@ where .delimited_by(just(Token::OpenBrace), just(Token::CloseBrace)) .labelled("map"); + let arg_list = select! { Token::Var(s) => s.to_owned() } + .repeated() + .collect() + .delimited_by(just(Token::OpenBracket), just(Token::CloseBracket)); + + let signature = choice(( + // ([x] [y]) + arg_list + .clone() + .labelled("input list") + .then(arg_list.clone().labelled("output list")), + // ([x]) + arg_list + .clone() + .labelled("input list") + .map(|x: Vec<_>| (x.clone(), x)), + // () + empty().map(|()| (vec![], vec![])), + )) + .delimited_by(just(Token::OpenParen), just(Token::CloseParen)) + .labelled("function signature"); + + let lambda = just(Token::Fn) + .ignore_then(signature.clone()) + .then(block.clone()) + .map(|((inputs, outputs), body)| Ast::Fn { + inputs, + outputs, + body, + }); + + // this is purely syntax sugar + let standalone_func = just(Token::Fn) + .ignore_then(ident.clone()) + .then(signature.clone()) + .then(block.clone()) + .map(|((id, (inputs, outputs)), body)| { + let lambda = Ast::Fn { + inputs, + outputs, + body, + }; + Ast::Set(id, Box::new(lambda)) + }) + .labelled("standalone function"); + let non_app = choice(( set, num, + standalone_func, + lambda, ident.map(Ast::Var).labelled("variable"), map, - block, + block.map(Ast::Block), )); let app = non_app diff --git a/src/val.rs b/src/val.rs index c478b95..e28584e 100644 --- a/src/val.rs +++ b/src/val.rs @@ -18,7 +18,7 @@ impl fmt::Debug for Val { Self::Number(n) => write!(f, "{n:?}"), Self::Function { inputs, outputs, .. - } => write!(f, "fn([{}], [{}]", inputs.join(" "), outputs.join(" ")), + } => write!(f, "fn([{}] [{}])", inputs.join(" "), outputs.join(" ")), Self::Map(map) => write!(f, "{map:?}"), } }