1+ use std::convert::TryFrom;
12use std::str::FromStr;
23use crate::time;
34use crate::parser::{ast, Expr, Stmt, ParseError, concat, prepend, call, self};
5+ use ast::AssignTarget;
46
57grammar;
68
@@ -277,13 +279,13 @@ FnExpr = { FnExpr1, FnExpr2 };
277279FnExpr1: Expr = <dyn_scoping:"dyn"?> <params:FnExpr1Params> <body:Either2<Expr, FnBlockNoReturn>> =>
278280 ast::FnExpr { params, body: body.into(), dynamic_scoping: dyn_scoping.is_some() }.into();
279281FnExpr1Params = {
280- "|" <List0<IdentTerm , ",">> "|",
282+ "|" <List0<AssignTarget , ",">> "|",
281283 // Zero arguments should be captures by the List0, but it isn't because `||` is pre-tokenized as OR
282284 "||" => vec![],
283285};
284286
285287// FnDef-like syntax, more suitable for chaining with FnDef: fn adder($a) = fn ($b) = $a+$b; adder(2)(3)
286- FnExpr2: Expr = <dyn_scoping:"dyn"?> "fn" <params:Paren<List0<IdentTerm , ",">>> <body:Either3<("=" <Expr>), BlockExpr, FnBlockNoReturn>> =>
288+ FnExpr2: Expr = <dyn_scoping:"dyn"?> "fn" <params:Paren<List0<AssignTarget , ",">>> <body:Either3<("=" <Expr>), BlockExpr, FnBlockNoReturn>> =>
287289 ast::FnExpr { params, body: body.into(), dynamic_scoping: dyn_scoping.is_some() }.into();
288290
289291// XXX Should probably settle on one syntax to rule them all. Maybe `($a, $b) => $a+$b`?
@@ -393,10 +395,10 @@ BtcAmount: Expr = <num:SExpr> <denom:BTC_DENOMINATION> =>
393395Assign: Stmt = "let"? <assigns:List1<Assignment, ",">> ";" =>
394396 ast::Assign(assigns).into();
395397
396- Assignment: ast::Assignment = <lhs:IdentTerm > "=" <rhs:Expr> =>
398+ Assignment: ast::Assignment = <lhs:AssignTarget > "=" <rhs:Expr> =>
397399 ast::Assignment { lhs, rhs };
398400
399- FnDef: Stmt = <dyn_scoping:"dyn"?> "fn" <ident:IdentTerm> <params:Paren<List0<IdentTerm , ",">>> <body:FnDefBody> =>
401+ FnDef: Stmt = <dyn_scoping:"dyn"?> "fn" <ident:IdentTerm> <params:Paren<List0<AssignTarget , ",">>> <body:FnDefBody> =>
400402 ast::FnDef { ident, params, body, dynamic_scoping: dyn_scoping.is_some() }.into();
401403
402404FnDefBody = { "=" <Expr> ";", <BlockExpr> ";"?, <FnBlockNoReturn> ";"? };
@@ -409,15 +411,23 @@ IfStmtElse: Vec<Stmt> = {
409411 "else" <IfStmt> => vec![<>],
410412};
411413
412- // An expression used in a statement position. The evaluated return value is
413- // discarded, but this can be useful for expressions that produce side effects
414- // like logging and exceptions.
414+ // An expression used in a statement position. The evaluated return value is discarded,
415+ // but this can be useful for expressions that produce logging/exceptions side effects.
415416ExprStmt: Stmt = <Expr> ";" => ast::ExprStmt(<>).into();
416417
418+ // Used as the target for Assignment and function parameters
419+ AssignTarget: AssignTarget = {
420+ IdentTerm => AssignTarget::Ident(<>),
421+ // Reuses the existing Expr Array type as an AssignTarget to play nicely with LR(1) grammar.
422+ // ASsignTarget::try_from() will reject Arrays that are not valid as a target.
423+ // https://github.com/lalrpop/lalrpop/issues/552#issuecomment-778923903
424+ Array =>? Ok(AssignTarget::try_from(<>)?),
425+ };
426+
417427// Helpers
418428
419429Either2<A, B>: Expr = { A, B };
420- Either3<A, B, C>: Expr = { A, B };
430+ Either3<A, B, C>: Expr = { A, B, C };
421431
422432// A `S`-separated list of zero or more `T` values
423433List0<T, S>: Vec<T> = <l:(<T> S)*> <t:T?> => concat(l, t);
0 commit comments