diff --git a/gold/src/parsing.rs b/gold/src/parsing.rs index 5f8251b..78bbad7 100644 --- a/gold/src/parsing.rs +++ b/gold/src/parsing.rs @@ -69,11 +69,6 @@ impl<'a> FromExternalError, ParseFloatError> for SyntaxError { } -// fn literal(x: T) -> Expr where Object: From { -// Object::from(x).literal() -// } - - /// Convert a multiline string from source code to string by removing leading /// whitespace from each line according to the rules for such strings. fn multiline(s: &str) -> String { @@ -190,6 +185,13 @@ trait Parser<'a, T>: NomParser, T, SyntaxError> {} impl<'a, T, P> Parser<'a, T> for P where P: NomParser, T, SyntaxError> {} +/// Parser that always succeeds and consumes nothing +fn success<'a>(input: In<'a>) -> Out<'a, Tagged<&'a str>> { + let loc = input.position(); + Ok((input, "".tag(loc.with_length(0)))) +} + + /// Convert errors to failures. fn fail<'a, O, T>( mut parser: impl Parser<'a, O>, @@ -488,7 +490,7 @@ fn map_keyword<'a>(value: &'a str) -> impl Parser<'a, Tagged<&'a str>> { /// List of keywords that must be avoided by the [`identifier`] parser. -static KEYWORDS: [&'static str; 16] = [ +static KEYWORDS: [&'static str; 17] = [ "for", "when", "if", @@ -505,6 +507,7 @@ static KEYWORDS: [&'static str; 16] = [ "not", "as", "import", + "fn", ]; @@ -1548,23 +1551,83 @@ fn binding<'a>(input: In<'a>) -> Out<'a, Tagged> { (TokenType::CloseBrace, SyntaxElement::MapBindingElement), (TokenType::CloseBrace, TokenType::Comma), ), - |x| { - x.wrap(Binding::Map) - // let loc = x.span(); - // x.wrap(Binding::Map, loc) - }, + |x| x.wrap(Binding::Map), ) ))(input) } +/// Matches a function definition. +/// +/// This is the 'fn' keyword followed by either an open paren or brace. +/// +/// An open paren must be followed by a list binding and optionally a semicolon +/// and a map binding, then a close paren and an expression. +/// +/// An open brace must be followed by a map binding, a close brace and an +/// expression. +fn function_new_style<'a>(input: In<'a>) -> Out<'a, PExpr> { + let (i, init) = keyword("fn").parse(input)?; + let (i, opener) = fail( + alt((open_paren, open_brace)), + (TokenType::OpenParen, TokenType::OpenBrace), + ).parse(i)?; + + let (i, args, kwargs, expr) = if opener.unwrap() == "(" { + // Parse a normal function + let (i, (args, end)) = list_binding( + success, + |i| alt((close_paren, semicolon))(i), + (TokenType::CloseParen, TokenType::SemiColon, SyntaxElement::PosParam), + (TokenType::CloseParen, TokenType::SemiColon, TokenType::Comma), + ).parse(i)?; + + let (i, kwargs) = if end == ";" { + let (i, kwargs) = map_binding( + success, + |i| close_paren(i), + (TokenType::CloseParen, SyntaxElement::KeywordParam), + (TokenType::CloseParen, TokenType::Comma), + ).parse(i)?; + (i, Some(kwargs)) + } else { + (i, None) + }; + + let (i, expr) = fail(expression, SyntaxElement::Expression).parse(i)?; + + (i, args.unwrap(), kwargs, expr) + } else { + // Parse a keyword function + let (i, kwargs) = map_binding( + success, + |i| close_brace(i), + (TokenType::CloseBrace, SyntaxElement::KeywordParam), + (TokenType::CloseBrace, TokenType::Comma), + ).parse(i)?; + let (i, expr) = fail(expression, SyntaxElement::Expression).parse(i)?; + + (i, ListBinding(vec![]), Some(kwargs), expr) + }; + + let span = init.span()..expr.outer(); + let result = PExpr::Naked(Expr::Function { + positional: args, + keywords: kwargs.map(Tagged::unwrap), + expression: Box::new(expr.inner()), + }.tag(span)); + + Ok((i, result)) +} + + /// Matches a standard function definition. /// /// This is the 'fn' keyword followed by a list binding and an optional map /// binding, each with slightly different delimiters from conventional /// let-binding syntax. It is concluded by a double arrow (=>) and an /// expression. -fn normal_function<'a>(input: In<'a>) -> Out<'a, PExpr> { +fn normal_function_old_style<'a>(input: In<'a>) -> Out<'a, PExpr> { let (i, (args, end)) = list_binding( |i| pipe(i), |i| alt((pipe, semicolon))(i), @@ -1572,12 +1635,10 @@ fn normal_function<'a>(input: In<'a>) -> Out<'a, PExpr> { (TokenType::Pipe, TokenType::SemiColon, TokenType::Comma), ).parse(input)?; - // println!("parsing normal function, end is {:?}", end); - let (j, kwargs) = if end == ";" { - // println!("keyword args"); let (j, kwargs) = map_binding( - |i: In<'a>| { let loc = i.position(); Ok((i, "".tag(loc.with_length(0)))) }, + success, + // |i: In<'a>| { let loc = i.position(); Ok((i, "".tag(loc.with_length(0)))) }, |i| pipe(i), (TokenType::Pipe, SyntaxElement::KeywordParam), (TokenType::Pipe, TokenType::Comma), @@ -1596,6 +1657,7 @@ fn normal_function<'a>(input: In<'a>) -> Out<'a, PExpr> { expression: expr.inner().to_box(), }.tag(span)); + eprintln!("gold: |...| syntax is deprecated, use fn (...) instead"); Ok((l, result)) } @@ -1604,7 +1666,7 @@ fn normal_function<'a>(input: In<'a>) -> Out<'a, PExpr> { /// /// This is a conventional map binding followed by a double arrow (=>) and an /// expression. -fn keyword_function<'a>(input: In<'a>) -> Out<'a, PExpr> { +fn keyword_function_old_style<'a>(input: In<'a>) -> Out<'a, PExpr> { map( tuple(( map_binding( @@ -1618,6 +1680,7 @@ fn keyword_function<'a>(input: In<'a>) -> Out<'a, PExpr> { |(kwargs, expr)| { let span = kwargs.span()..expr.outer(); + eprintln!("gold: {{|...|}} syntax is deprecated, use fn {{...}} instead"); PExpr::Naked(Expr::Function { positional: ListBinding(vec![]), keywords: Some(kwargs.unwrap()), @@ -1634,8 +1697,9 @@ fn keyword_function<'a>(input: In<'a>) -> Out<'a, PExpr> { /// [`keyword_function`]. fn function<'a>(input: In<'a>) -> Out<'a, PExpr> { alt(( - keyword_function, - normal_function, + function_new_style, + keyword_function_old_style, + normal_function_old_style, ))(input) } diff --git a/gold/src/tests/eval.rs b/gold/src/tests/eval.rs index 300ac7d..50b439a 100644 --- a/gold/src/tests/eval.rs +++ b/gold/src/tests/eval.rs @@ -209,51 +209,51 @@ fn map_bindings() { #[test] fn function_bindings() { assert_seq!(eval(concat!( - "let a = |x, [y, z]| x + y + z\n", + "let a = fn (x, [y, z]) x + y + z\n", "in a(1, [2, 3])" )), Object::int(6)); assert_seq!(eval(concat!( - "let f = |[y = 1]| y\n", + "let f = fn ([y = 1]) y\n", "in f([])" )), Object::int(1)); assert_seq!(eval(concat!( "let q = 1\n", - "let f = |[y = q]| y\n", + "let f = fn ([y = q]) y\n", "in f([])" )), Object::int(1)); assert_seq!(eval(concat!( - "let f = |q| |[y = q]| y\n", + "let f = fn (q) fn ([y = q]) y\n", "let q = 1\n", "in f(2)([])" )), Object::int(2)); assert_seq!(eval(concat!( - "let f = |x; y, z| x + y + z\n", + "let f = fn (x; y, z) x + y + z\n", "in f(1, y: 2, z: 3)" )), Object::int(6)); assert_seq!(eval(concat!( - "let f = |; y=1| y\n", + "let f = fn (; y=1) y\n", "in f()" )), Object::int(1)); assert_seq!(eval(concat!( "let q = 1\n", - "let f = |; y=q| y\n", + "let f = fn (; y=q) y\n", "in f()" )), Object::int(1)); assert_seq!(eval(concat!( - "let f = |q| |; y=q| y\n", + "let f = fn (q) fn (; y=q) y\n", "let q = 1\n", "in f(2)()" )), Object::int(2)); assert_seq!(eval(concat!( - "let f = |x, y=15; z=200| [x,y,z]\n", + "let f = fn (x, y=15; z=200) [x,y,z]\n", "in [f(1), f(1,2), f(1,z:100), f(1,2,z:100)]" )), Object::list(vec![ Object::list(vec![Object::int(1), Object::int(15), Object::int(200)]), @@ -263,7 +263,7 @@ fn function_bindings() { ])); assert_seq!(eval(concat!( - "let dest = |...args; ...kwargs| [args, kwargs]\n", + "let dest = fn (...args; ...kwargs) [args, kwargs]\n", "in dest()" )), Object::list(vec![ Object::list(()), @@ -271,7 +271,7 @@ fn function_bindings() { ])); assert_seq!(eval(concat!( - "let dest = |...args; ...kwargs| [args, kwargs]\n", + "let dest = fn (...args; ...kwargs) [args, kwargs]\n", "in dest(1, 2)" )), Object::list(vec![ (1..3).map(Object::int).collect(), @@ -279,7 +279,7 @@ fn function_bindings() { ])); assert_seq!(eval(concat!( - "let dest = |...args; ...kwargs| [args, kwargs]\n", + "let dest = fn (...args; ...kwargs) [args, kwargs]\n", "in dest(x: 1, y: 2)" )), Object::list(vec![ Object::list(()), @@ -290,7 +290,7 @@ fn function_bindings() { ])); assert_seq!(eval(concat!( - "let dest = |...args; ...kwargs| [args, kwargs]\n", + "let dest = fn (...args; ...kwargs) [args, kwargs]\n", "in dest(1, 2, x: 3, y: 4)" )), Object::list(vec![ (1..3).map(Object::int).collect(), @@ -301,7 +301,7 @@ fn function_bindings() { ])); assert_seq!(eval(concat!( - "let dest = |...args; ...kwargs| [args, kwargs]\n", + "let dest = fn (...args; ...kwargs) [args, kwargs]\n", "let args = [1, 2, 3]\n", "let kwargs = {x: 4, y: 5, z: 6}\n", "in dest(0, ...args, 5, a: 8, ...kwargs, c: 10, z: 12)" @@ -322,10 +322,10 @@ fn function_bindings() { ]), ])); - assert_seq!(eval("({||} 1)()"), Object::int(1)); - assert_seq!(eval("({|a, b|} a + b)(a: 1, b: 2)"), Object::int(3)); - assert_seq!(eval("({|a, b=2|} a + b)(a: 1, b: 3)"), Object::int(4)); - assert_seq!(eval("({|a, b=2|} a + b)(a: 1)"), Object::int(3)); + assert_seq!(eval("(fn {} 1)()"), Object::int(1)); + assert_seq!(eval("(fn {a, b} a + b)(a: 1, b: 2)"), Object::int(3)); + assert_seq!(eval("(fn {a, b=2} a + b)(a: 1, b: 3)"), Object::int(4)); + assert_seq!(eval("(fn {a, b=2} a + b)(a: 1)"), Object::int(3)); } @@ -505,8 +505,8 @@ fn map_concat() { #[test] fn functions() { assert_seq!(eval(concat!( - "let double = |x| x + x\n", - "let applytwice = |f,x| f(f(x))\n", + "let double = fn (x) x + x\n", + "let applytwice = fn (f,x) f(f(x))\n", "in applytwice(double, [1])" )), Object::list(vec![ Object::int(1), @@ -517,62 +517,62 @@ fn functions() { assert_seq!(eval(concat!( "let a = 1\n", - "let b = || a\n", + "let b = fn () a\n", "let a = 2\n", "in b()" )), Object::int(1)); assert_seq!(eval(concat!( "let a = 1\n", - "let b = |q = a| q\n", + "let b = fn (q = a) q\n", "in b()" )), Object::int(1)); assert_seq!(eval(concat!( "let a = 1\n", - "let b = |q = a| q\n", + "let b = fn (q = a) q\n", "let a = 2\n", "in b()" )), Object::int(1)); assert_seq!(eval(concat!( - "let b = || let a = 1 in |q = a| q\n", + "let b = fn () let a = 1 in fn (q = a) q\n", "let c = b()\n", "in c()" )), Object::int(1)); assert_seq!(eval(concat!( - "let a = |q, ...x| [q, ...x]\n", + "let a = fn (q, ...x) [q, ...x]\n", "in a(1, 2, 3)" )), (1..4).map(Object::int).collect()); assert_seq!(eval(concat!( - "let a = |q, p = q| p\n", + "let a = fn (q, p = q) p\n", "in a(1, 2)" )), Object::int(2)); assert_seq!(eval(concat!( - "let a = |q, p = q| p\n", + "let a = fn (q, p = q) p\n", "in a(1)" )), Object::int(1)); assert_seq!(eval(concat!( - "let a = |; k = 1| k\n", + "let a = fn (; k = 1) k\n", "in a()" )), Object::int(1)); assert_seq!(eval(concat!( - "let a = |; k = 1| k\n", + "let a = fn (; k = 1) k\n", "in a(k: 2)" )), Object::int(2)); assert_seq!(eval(concat!( - "let a = {|k = 1|} k\n", + "let a = fn {k = 1} k\n", "in a()" )), Object::int(1)); assert_seq!(eval(concat!( - "let a = {|k = 1|} k\n", + "let a = fn {k = 1} k\n", "in a(k: 2)" )), Object::int(2)); } @@ -731,7 +731,7 @@ fn errors() { assert_eq!(eval("{$null: 1}"), err!(TypeMismatch::MapKey(Type::Null), loc!(2..6, Assign))); assert_eq!(eval("{...[]}"), err!(TypeMismatch::SplatMap(Type::List), loc!(4..6, Splat))); assert_eq!(eval("{for x in 2.2: a: x}"), err!(TypeMismatch::Iterate(Type::Float), loc!(10..13, Iterate))); - assert_eq!(eval("(|...x| 1)(...true)"), err!(TypeMismatch::SplatArg(Type::Boolean), loc!(14..18, Splat))); + assert_eq!(eval("(fn (...x) 1)(...true)"), err!(TypeMismatch::SplatArg(Type::Boolean), loc!(17..21, Splat))); assert_eq!(eval("1 + true"), err!(TypeMismatch::BinOp(Type::Integer, Type::Boolean, BinOp::Add), loc!(2, Evaluate))); assert_eq!(eval("\"t\" - 9"), err!(TypeMismatch::BinOp(Type::String, Type::Integer, BinOp::Subtract), loc!(4, Evaluate))); assert_eq!(eval("[] * 9"), err!(TypeMismatch::BinOp(Type::List, Type::Integer, BinOp::Multiply), loc!(3, Evaluate))); @@ -777,20 +777,20 @@ fn errors() { assert!(eval_errstr("\n bingbong \n").is_some_and(|x| x.contains("\n bingbong \n ^^^^^^^^\n"))); assert!(eval_errstr(concat!( - "let f = |x| x + 1\n", - "let g = |x| f(x)\n", - "let h = |x| g(x)\n", + "let f = fn (x) x + 1\n", + "let g = fn (x) f(x)\n", + "let h = fn (x) g(x)\n", "in h(null)", )).is_some_and(|x| x.contains(concat!( - "let f = |x| x + 1\n", - " ^", + "let f = fn (x) x + 1\n", + " ^", )) && x.contains(concat!( - "let g = |x| f(x)\n", - " ^^^", + "let g = fn (x) f(x)\n", + " ^^^", )) && x.contains(concat!( - "let h = |x| g(x)\n", - " ^^^", + "let h = fn (x) g(x)\n", + " ^^^", )) && x.contains(concat!( "in h(null)\n", " ^^^^^", diff --git a/gold/src/tests/parsing.rs b/gold/src/tests/parsing.rs index af1965a..219248f 100644 --- a/gold/src/tests/parsing.rs +++ b/gold/src/tests/parsing.rs @@ -912,25 +912,25 @@ fn funcall() { ); assert_eq!( - parse("(|x,y| x+y)(1,2)"), + parse("(fn (x,y) x+y)(1,2)"), Ok( Expr::Function { positional: ListBinding(vec![ ListBindingElement::Binding { - binding: "x".bid(2), + binding: "x".bid(5), default: None - }.tag(2), + }.tag(5), ListBindingElement::Binding { - binding: "y".bid(4), + binding: "y".bid(7), default: None - }.tag(4), + }.tag(7), ]), keywords: None, - expression: "x".id(7).add("y".id(9), 8).tag(7..10).to_box(), - }.tag(1..10).funcall(vec![ - 1.expr(12).wrap(ArgElement::Singleton), - 2.expr(14).wrap(ArgElement::Singleton), - ], 11..16).tag(0..16) + expression: "x".id(10).add("y".id(12), 11).tag(10..13).to_box(), + }.tag(1..13).funcall(vec![ + 1.expr(15).wrap(ArgElement::Singleton), + 2.expr(17).wrap(ArgElement::Singleton), + ], 14..19).tag(0..19) ), ); @@ -1121,72 +1121,72 @@ fn operators() { #[test] fn functions() { assert_eq!( - parse("|| 1"), + parse("fn () 1"), Ok(Expr::Function { positional: ListBinding(vec![]), keywords: None, - expression: 1.expr(3).to_box(), - }.tag(0..4)), + expression: 1.expr(6).to_box(), + }.tag(0..7)), ); assert_eq!( - parse("|;| 1"), + parse("fn (;) 1"), Ok(Expr::Function { positional: ListBinding(vec![]), keywords: Some(MapBinding(vec![])), - expression: 1.expr(4).to_box(), - }.tag(0..5)), + expression: 1.expr(7).to_box(), + }.tag(0..8)), ); assert_eq!( - parse("{||} 1"), + parse("fn {} 1"), Ok(Expr::Function { positional: ListBinding(vec![]), keywords: Some(MapBinding(vec![])), - expression: 1.expr(5).to_box(), - }.tag(0..6)), + expression: 1.expr(6).to_box(), + }.tag(0..7)), ); assert_eq!( - parse("|a| let b = a in b"), + parse("fn (a) let b = a in b"), Ok(Expr::Function { positional: ListBinding(vec![ ListBindingElement::Binding { - binding: "a".bid(1), + binding: "a".bid(4), default: None - }.tag(1), + }.tag(4), ]), keywords: None, expression: Box::new(Expr::Let { bindings: vec![ ( - "b".bid(8), - "a".id(12), + "b".bid(11), + "a".id(15), ), ], - expression: "b".id(17).to_box(), - }.tag(4..18)), - }.tag(0..18)), + expression: "b".id(20).to_box(), + }.tag(7..21)), + }.tag(0..21)), ); assert_eq!( - parse("{|x=1, y=2|} x + y"), + parse("fn {x=1, y=2} x + y"), Ok(Expr::Function { positional: ListBinding(vec![]), keywords: Some(MapBinding(vec![ MapBindingElement::Binding { - key: "x".key(2), - binding: "x".bid(2), - default: Some(1.expr(4)), - }.tag(2..5), + key: "x".key(4), + binding: "x".bid(4), + default: Some(1.expr(6)), + }.tag(4..7), MapBindingElement::Binding { - key: "y".key(7), - binding: "y".bid(7), - default: Some(2.expr(9)), - }.tag(7..10), + key: "y".key(9), + binding: "y".bid(9), + default: Some(2.expr(11)), + }.tag(9..12), ])), - expression: "x".id(13).add("y".id(17), 15).tag(13..18).to_box(), - }.tag(0..18)), + expression: "x".id(14).add("y".id(18), 16).tag(14..19).to_box(), + }.tag(0..19)), ); } @@ -1295,17 +1295,17 @@ fn errors() { err!("(", 1, S::Expression); err!("(1", 2, T::CloseParen); - err!("|", 1, T::Pipe, T::SemiColon, S::PosParam); - err!("|x", 2, T::Pipe, T::SemiColon, T::Comma); - err!("|x,", 3, T::Pipe, T::SemiColon, S::PosParam); - err!("|;", 2, T::Pipe, S::KeywordParam); - err!("|;y", 3, T::Pipe, T::Comma); - err!("|;y,", 4, T::Pipe, S::KeywordParam); - err!("||", 2, S::Expression); - err!("{|", 2, T::CloseBracePipe, S::KeywordParam); - err!("{|x", 3, T::CloseBracePipe, T::Comma); - err!("{|x,", 4, T::CloseBracePipe, S::KeywordParam); - err!("{||}", 4, S::Expression); + err!("fn (", 4, T::CloseParen, T::SemiColon, S::PosParam); + err!("fn (x", 5, T::CloseParen, T::SemiColon, T::Comma); + err!("fn (x,", 6, T::CloseParen, T::SemiColon, S::PosParam); + err!("fn (;", 5, T::CloseParen, S::KeywordParam); + err!("fn (;y", 6, T::CloseParen, T::Comma); + err!("fn (;y,", 7, T::CloseParen, S::KeywordParam); + err!("fn ()", 5, S::Expression); + err!("fn {", 4, T::CloseBrace, S::KeywordParam); + err!("fn {x", 5, T::CloseBrace, T::Comma); + err!("fn {x,", 6, T::CloseBrace, S::KeywordParam); + err!("fn {}", 5, S::Expression); err!("\"alpha", 6, T::DoubleQuote); err!("\"alpha$", 7, T::OpenBrace);