From b06f2f052f38cb95abd64af4f7e7e2be66547f19 Mon Sep 17 00:00:00 2001 From: mehbark Date: Sat, 3 Jan 2026 00:45:46 -0500 Subject: [PATCH] add access syntax --- src/ast.rs | 5 ++++ src/parser.rs | 28 +++++++++++++++----- src/tests/parser.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 24a65de..0dccc70 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -72,6 +72,8 @@ pub enum Ast { Num(f64), /// `(println bla; 3) #=> 3` Block(Vec), + /// `std.println` + Access(Box, Symbol), } impl fmt::Display for Ast { @@ -108,6 +110,9 @@ impl fmt::Display for Ast { let body: Vec<_> = body.iter().map(|stmt| format!("{stmt}")).collect(); write!(f, "({})", body.join(" ")) } + Ast::Access(val, sym) => { + write!(f, "{val}.{sym}") + } } } } diff --git a/src/parser.rs b/src/parser.rs index 2cff09c..2e6dbc8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -18,6 +18,9 @@ enum Token<'a> { #[token(":")] Colon, + #[token(".")] + Period, + #[token(",")] Comma, @@ -64,6 +67,7 @@ impl fmt::Display for Token<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Token::Colon => write!(f, ":"), + Token::Period => write!(f, "."), Token::Comma => write!(f, ","), Token::Semicolon => write!(f, ";"), Token::Set => write!(f, ":="), @@ -99,6 +103,8 @@ where recursive(|expr| { let expr = expr.labelled("expression"); + let symbol = select! { Token::Var(s) => s.to_owned() }; + let ident = choice(( just(Token::Return) .ignore_then(select! { @@ -194,7 +200,7 @@ where }) .labelled("standalone function"); - let non_app = choice(( + let e1 = choice(( set, num, standalone_func, @@ -202,18 +208,28 @@ where ident.map(Ast::Var).labelled("variable"), map, block.map(Ast::Block), - )); + )) + .boxed(); - let app = non_app + let access = e1 .clone() - .foldl(non_app.clone().repeated(), |f, x| { + .then_ignore(just(Token::Period)) + .foldl(symbol.separated_by(just(Token::Period)), |a, b| { + Ast::Access(Box::new(a), b) + }) + .labelled("access"); + + let e2 = access.or(e1); + + let app = e2 + .clone() + .foldl(e2.clone().repeated(), |f, x| { Ast::App(Box::new(f), Box::new(x)) }) .labelled("function application"); - let atom = app.or(non_app); + let atom = app.or(e2); - // % atom.pratt(( infix(none(1), just(Token::BinOp("=")), |l, _, r, _| { Ast::BinOp(Box::new(l), BinOp::Eq, Box::new(r)) diff --git a/src/tests/parser.rs b/src/tests/parser.rs index 723e858..0be8736 100644 --- a/src/tests/parser.rs +++ b/src/tests/parser.rs @@ -134,3 +134,66 @@ foo := 3; ", ); } + +#[test] +fn oop() { + test( + " +fn make_counter([] [obj]) ( + i := 0; + { + fn ^inc() ( + i := i + 1; + ); + + fn ^dec() ( + i := i - 1; + ); + + fn ^get([] [i]) (); + } +); + +counter_a := make_counter {}; +counter_b := make_counter {}; + +counter_a.inc {}; +std.println(counter_a.get {}); +counter_a.inc {}; +std.println(counter_a.get {}); +counter_a.inc {}; +std.println(counter_a.get {}); +counter_a.dec {}; +std.println(counter_a.get {}); + +counter_b.inc {}; +std.println(counter_b.get {}); +counter_b.dec {}; +", + " +(set! make_counter \ + (fn ([] [obj]) \ + (set! i 0) \ + {\ + (set! ^inc (fn ([] []) (set! i (i + 1)))) \ + (set! ^dec (fn ([] []) (set! i (i - 1)))) \ + (set! ^get (fn ([] [i])))\ + }\ + )\ +); +(set! counter_a (make_counter {})); +(set! counter_b (make_counter {})); +(counter_a.inc {}); +(std.println ((counter_a.get {}))); +(counter_a.inc {}); +(std.println ((counter_a.get {}))); +(counter_a.inc {}); +(std.println ((counter_a.get {}))); +(counter_a.dec {}); +(std.println ((counter_a.get {}))); +(counter_b.inc {}); +(std.println ((counter_b.get {}))); +(counter_b.dec {}) +", + ); +}