diff --git a/book/src/list-functions-lists.md b/book/src/list-functions-lists.md
index ad9a7399..10723fa0 100644
--- a/book/src/list-functions-lists.md
+++ b/book/src/list-functions-lists.md
@@ -34,7 +34,7 @@ fn cons(x: A, xs: List) -> List
Append an element to the end of a list.
```nbt
-fn cons_end(xs: List, x: A) -> List
+fn cons_end(x: A, xs: List) -> List
```
### `is_empty`
diff --git a/examples/tests/lists.nbt b/examples/tests/lists.nbt
index 22ba39c6..f4043c02 100644
--- a/examples/tests/lists.nbt
+++ b/examples/tests/lists.nbt
@@ -10,8 +10,8 @@ assert_eq(tail(xs), [2, 3])
assert_eq(cons(0, xs), [0, 1, 2, 3])
-assert_eq(cons_end(xs, 4), [1, 2, 3, 4])
-assert_eq(cons_end([], 0), [0])
+assert_eq(cons_end(4, xs), [1, 2, 3, 4])
+assert_eq(cons_end(0, []), [0])
assert(is_empty([]))
assert(!is_empty(xs))
diff --git a/numbat/modules/core/lists.nbt b/numbat/modules/core/lists.nbt
index 3cba05e8..24e2217e 100644
--- a/numbat/modules/core/lists.nbt
+++ b/numbat/modules/core/lists.nbt
@@ -15,7 +15,7 @@ fn tail(xs: List) -> List
fn cons(x: A, xs: List) -> List
@description("Append an element to the end of a list")
-fn cons_end(xs: List, x: A) -> List
+fn cons_end(x: A, xs: List) -> List
@description("Check if a list is empty")
fn is_empty(xs: List) -> Bool = xs == []
@@ -55,7 +55,7 @@ fn range(start: Scalar, end: Scalar) -> List =
fn reverse(xs: List) -> List =
if is_empty(xs)
then []
- else cons_end(reverse(tail(xs)), head(xs))
+ else cons_end(head(xs), reverse(tail(xs)))
@description("Generate a new list by applying a function to each element of the input list")
fn map(f: Fn[(A) -> B], xs: List) -> List =
diff --git a/numbat/src/ffi/lists.rs b/numbat/src/ffi/lists.rs
index ae4e0292..609465c9 100644
--- a/numbat/src/ffi/lists.rs
+++ b/numbat/src/ffi/lists.rs
@@ -36,8 +36,8 @@ pub fn cons(mut args: Args) -> Result {
}
pub fn cons_end(mut args: Args) -> Result {
- let mut list = list_arg!(args);
let element = arg!(args);
+ let mut list = list_arg!(args);
list.push_back(element);
return_list!(list)
diff --git a/numbat/src/parser.rs b/numbat/src/parser.rs
index 04bad6d3..c4917301 100644
--- a/numbat/src/parser.rs
+++ b/numbat/src/parser.rs
@@ -108,6 +108,9 @@ pub enum ParseErrorKind {
#[error("Expected identifier")]
ExpectedIdentifier,
+ #[error("Expected identifier or function call after postfix apply (`//`)")]
+ ExpectedIdentifierOrCallAfterPostfixApply,
+
#[error("Expected dimension identifier, '1', or opening parenthesis")]
ExpectedDimensionPrimary,
@@ -880,16 +883,30 @@ impl<'a> Parser<'a> {
let mut expr = self.condition()?;
let mut full_span = expr.full_span();
while self.match_exact(TokenKind::PostfixApply).is_some() {
- let identifier = self.identifier()?;
- let identifier_span = self.last().unwrap().span;
- full_span = full_span.extend(&identifier_span);
-
- expr = Expression::FunctionCall(
- identifier_span,
- full_span,
- Box::new(Expression::Identifier(identifier_span, identifier)),
- vec![expr],
- );
+ match self.call()? {
+ Expression::Identifier(span, ident) => {
+ full_span = full_span.extend(&span);
+
+ expr = Expression::FunctionCall(
+ span,
+ full_span,
+ Box::new(Expression::Identifier(span, ident)),
+ vec![expr],
+ );
+ }
+ Expression::FunctionCall(call_span, fn_full_span, call, mut params) => {
+ full_span = full_span.extend(&fn_full_span);
+
+ params.push(expr);
+ expr = Expression::FunctionCall(call_span, full_span, call, params);
+ }
+ _other => {
+ return Err(ParseError::new(
+ ParseErrorKind::ExpectedIdentifierOrCallAfterPostfixApply,
+ full_span,
+ ))
+ }
+ }
}
Ok(expr)
}
@@ -1857,7 +1874,7 @@ mod tests {
for input in inputs {
match parse(input, 0) {
Err((_, errors)) => {
- assert_eq!(errors[0].kind, error_kind);
+ assert_eq!(errors[0].kind, error_kind, "Failed on {}", input);
}
_ => {
panic!();
@@ -2734,6 +2751,24 @@ mod tests {
vec![binop!(scalar!(1.0), Add, scalar!(1.0))],
),
);
+ parse_as_expression(
+ &["1 + 1 // kefir(2)"],
+ Expression::FunctionCall(
+ Span::dummy(),
+ Span::dummy(),
+ Box::new(identifier!("kefir")),
+ vec![scalar!(2.0), binop!(scalar!(1.0), Add, scalar!(1.0))],
+ ),
+ );
+
+ should_fail_with(&["1 // print()"], ParseErrorKind::InlineProcedureUsage);
+
+ should_fail_with(&["1 // +"], ParseErrorKind::ExpectedPrimary);
+
+ should_fail_with(
+ &["1 // 2", "1 // 1 +"],
+ ParseErrorKind::ExpectedIdentifierOrCallAfterPostfixApply,
+ );
}
#[test]