From 255c9f2109d73c98d1195abb202bbb8465ad2d0c Mon Sep 17 00:00:00 2001 From: mehbark Date: Fri, 2 Jan 2026 00:52:57 -0500 Subject: [PATCH] parse function application --- README.md | 20 +++++++++++++++++++ src/parser.rs | 54 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index ed0942a..a763a9f 100644 --- a/README.md +++ b/README.md @@ -73,3 +73,23 @@ compose f g x == (((compose f) g) x) == compose {f: f2, g} x == compose {x} f g ```rust fn id([x]) () ``` + +"Maps" should really just be blocks +```rust +foo := 3; +{ + x := 42; + ^twice_x := x * 2; + ^thrice_x := x * 3; + ^foo; +} +``` +becomes +```json5 +{ + "twice_x": 84, + "thrice_x": 126, + // ^foo as a statement behaves like ^foo := foo, so we get that nice record field punning + "foo": 3, +} +``` diff --git a/src/parser.rs b/src/parser.rs index ecb4145..af07122 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -17,6 +17,9 @@ enum Token<'a> { #[token(":")] Colon, + #[token(",")] + Comma, + #[token(";")] Semicolon, @@ -44,9 +47,6 @@ enum Token<'a> { #[regex(r"[+*/%=-]")] BinOp(&'a str), - #[token("fn")] - Fn, - #[regex(r"[a-zA-Z_][a-zA-Z_0-9']*")] Var(&'a str), @@ -60,6 +60,7 @@ impl fmt::Display for Token<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Token::Colon => write!(f, ":"), + Token::Comma => write!(f, ","), Token::Semicolon => write!(f, ";"), Token::Set => write!(f, ":="), Token::OpenParen => write!(f, "("), @@ -69,7 +70,6 @@ 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"), } @@ -85,14 +85,24 @@ fn parser<'tokens, 'src: 'tokens, I>() where I: ValueInput<'tokens, Token = Token<'src>, Span = SimpleSpan>, { + let semicolon = just(Token::Semicolon) + .repeated() + .at_least(1) + .labelled("semicolon"); + recursive(|expr| { + let expr = expr.labelled("expression"); + let ident = choice(( - just(Token::Return).ignore_then(select! { - Token::Var(s) => Ident::Return(s.to_owned()) - }), + just(Token::Return) + .ignore_then(select! { + Token::Var(s) => Ident::Return(s.to_owned()) + }) + .labelled("return identifier"), select! { Token::Var(s) => Ident::Local(s.to_owned()), - }, + } + .labelled("local identifier"), )) .labelled("identifier"); @@ -115,22 +125,28 @@ where }) .labelled("number"); - let semicolon = just(Token::Semicolon) - .repeated() - .at_least(1) - .labelled("semicolon"); - - let block = just(Token::OpenParen) - .ignore_then(expr.clone().separated_by(semicolon).collect()) - .then_ignore(just(Token::CloseParen)) + let block = expr + .clone() + .separated_by(semicolon.clone()) + .collect() .map(Ast::Block) + .delimited_by(just(Token::OpenParen), just(Token::CloseParen)) .labelled("block"); - choice((set, ident.map(Ast::Var).labelled("variable"), num, block)) + let non_app = choice((set, num, ident.map(Ast::Var).labelled("variable"), block)); + + let app = non_app + .clone() + .foldl(non_app.clone().repeated(), |f, x| { + Ast::App(Box::new(f), Box::new(x)) + }) + .labelled("function application"); + + app.or(non_app) }) .labelled("expression") - .then_ignore(just(Token::Semicolon).repeated().at_least(1)) - .repeated() + .separated_by(semicolon) + .allow_trailing() .collect() }