Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ has its own README worth reading before making non-trivial changes there (`src/p

## Block syntax (post indent/dedent removal)

`for`/`while`/`with` bodies always require an explicit `do ... end` block — there is no single-statement
`for`/`while`/`using` bodies always require an explicit `do ... end` block — there is no single-statement
shorthand for these three (`for a in b do c` is a parse error; it must be `for a in b do c end`). `if`/`then`/
`else` branches are the exception: each branch is parsed as one `parse_expr_or_stmt`, which accepts either a
bare single statement/expression or an explicit `do ... end` block (`if a then do ... end else c` is valid).
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ We can write a simple script that computes the factorial of a value given by the

```mamba
# Factorial of x
def factorial(x: Int) -> Int := match x with
def factorial(x: Int) -> Int := match x where
0 => 1
n => n * factorial(n - 1)
end
Expand All @@ -98,13 +98,13 @@ This means that the compiler will check for us that factorial is only used with
Also note that:

- Code blocks are denoted using `do` and `end` because this is a list of statements and expressions that gets executed _in order_.
- For a match expression or statement we denote cases starting with `with` and ending with `end`, as this is a _set_ of cases which we match on.
You can read `match x with ... end`, where we read this as "match `x` on this set of conditions in `with ... end`".
- For a match expression or statement we denote cases starting with `where` and ending with `end`, as this is a _set_ of cases which we match on.
You can read `match x where ... end`, where we read this as "match `x` on this set of conditions in `where ... end`".

_Note_ One could use [dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming) in the above example so that we consume less memory:

```mamba
def factorial(x: Int) -> Int := match x with
def factorial(x: Int) -> Int := match x where
0 => 1
n => do
def ans := 1
Expand Down Expand Up @@ -392,7 +392,7 @@ Take for instance this naive implementation of the Fibonacci sequence:

```mamba
## Fibonacci, implemented using recursion and not dynamic programming
def total pure fibonacci(x: PosInt) -> Int := match x with
def total pure fibonacci(x: PosInt) -> Int := match x where
0 => 0
1 => 1
n => fibonacci(n - 1) + fibonacci(n - 2)
Expand Down Expand Up @@ -549,7 +549,7 @@ This also prevents us from wrapping large code blocks in a `try`, where it might
Under the hood, `<call> ! where <cases> end` desugars to a plain `match` on the call's result:

```mamba
match m.last_op() with
match m.last_op() where
err: MatrixErr(message) => print("Error when getting last op: \"{message}\"")
end
```
Expand Down
8 changes: 4 additions & 4 deletions docs/features/control_flow/control_flow_expression.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,18 @@ We can even match based on the type of the returned expression.

A `match` has the form:

match <expression> with { <expression> => <expression or statement> }
match <expression> where { <expression> => <expression or statement> }

An example would be (if `b` is a number):

match b with
match b where
1 => print "one"
4 => print "four"
5 => print "five"

We can also add a default case if:

match b with
match b where
1 => print "one"
4 => print "four"
5 => print "five"
Expand All @@ -69,6 +69,6 @@ This can be achieved by either exhaustively covering every possible value, or by

So, a `match` _expression_ has the form:

match <expression> with { <expression> => <expression> }
match <expression> where { <expression> => <expression> }

With the additional requirement that we have an arm for every possible value of a given input type.
10 changes: 7 additions & 3 deletions docs/spec/grammar.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The grammar of the language in Extended Backus-Naur Form (EBNF).
| trait-def
| class-def
| import
| using-block
| "return" [ expression ]
| code-set
expression ::= control-flow-expr
Expand Down Expand Up @@ -101,14 +102,17 @@ The grammar of the language in Extended Backus-Naur Form (EBNF).

code-block ::= "do" expr-or-stmt { newline expr-or-stmt } "end"
code-set ::= "where" expr-or-stmt { newline expr-or-stmt } "end"
using-block ::= "using" expression { "as" expression } code-block

control-flow-expr::= if | match
if ::= "if" expression "then" expression [ "else" expression ]
match ::= "match" expression "with" map
match ::= "match" expression "where" match-cases
match-cases ::= "where" { match-case } "end"
match-case ::= expression "=>" expression

control-flow-stmt::= while | foreach | "break" | "continue"
while ::= "while" expression "do" expression
foreach ::= "for" expression "in" expression "do" expression
while ::= "while" expression code-block
foreach ::= "for" expression "in" expression code-block

newline ::= <platform dependent>
```
2 changes: 2 additions & 0 deletions docs/spec/reserved.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ Keyword | Use
## Blocks

Keyword | Use
---|---
`end` | Denote end of code block or set
`where` | Denote start of code set
`using` | Denote start of a resource block, binding an alias for the duration of its body

## 3.2.2 Special Characters

Expand Down
18 changes: 9 additions & 9 deletions src/backend/python/convert/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ pub fn convert_node(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &Context

NodeTy::Condition { .. } => return Err(Box::from(UnimplementedErr::new(ast, "condition"))),

NodeTy::With {
NodeTy::Using {
resource,
alias: Some((alias, ..)),
expr,
Expand All @@ -266,7 +266,7 @@ pub fn convert_node(ast: &ASTTy, imp: &mut Imports, state: &State, ctx: &Context
expr: Box::from(scope_guarded(expr, expr_core)),
}
}
NodeTy::With { resource, expr, .. } => {
NodeTy::Using { resource, expr, .. } => {
let expr_core = convert_node(expr, imp, state, ctx)?;
PythonCore::With {
resource: Box::from(convert_node(resource, imp, state, ctx)?),
Expand Down Expand Up @@ -803,7 +803,7 @@ mod tests {
}

#[test]
fn with_verify() {
fn using_verify() {
let resource = to_pos!(Node::Id {
lit: String::from("my_resource")
});
Expand All @@ -817,7 +817,7 @@ mod tests {
let expr = to_pos!(Node::Int {
lit: String::from("9")
});
let with = to_pos!(Node::With {
let using = to_pos!(Node::Using {
resource,
alias,
expr
Expand All @@ -827,9 +827,9 @@ mod tests {
resource,
alias,
expr,
}) = gen(&ASTTy::from(&with))
}) = gen(&ASTTy::from(&using))
else {
panic!("Expected with as but was {:?}", gen(&ASTTy::from(&with)))
panic!("Expected with as but was {:?}", gen(&ASTTy::from(&using)))
};

assert_eq!(
Expand Down Expand Up @@ -909,20 +909,20 @@ mod tests {
}

#[test]
fn with_no_as_verify() {
fn using_no_as_verify() {
let resource = to_pos!(Node::Id {
lit: String::from("other")
});
let expr = to_pos!(Node::Int {
lit: String::from("2341")
});
let with = to_pos!(Node::With {
let using = to_pos!(Node::Using {
resource,
alias: None,
expr
});

let (resource, expr) = match gen(&ASTTy::from(&with)) {
let (resource, expr) = match gen(&ASTTy::from(&using)) {
Ok(PythonCore::With { resource, expr }) => (resource, expr),
other => panic!("Expected with but was {other:?}"),
};
Expand Down
2 changes: 1 addition & 1 deletion src/check/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ pub enum NodeTy {
expr_or_stmt: Box<ASTTy>,
cases: Vec<ASTTy>,
},
With {
Using {
resource: Box<ASTTy>,
alias: Option<(Box<ASTTy>, bool, OptName)>,
expr: Box<ASTTy>,
Expand Down
4 changes: 2 additions & 2 deletions src/check/ast/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@ impl From<(&Node, &Finished)> for NodeTy {
.map(|ast| ASTTy::from((ast, finished)))
.collect(),
},
Node::With {
Node::Using {
resource,
alias,
expr,
} => NodeTy::With {
} => NodeTy::Using {
resource: Box::from(ASTTy::from((resource, finished))),
alias: alias.clone().map(|(resource, alias, expr)| {
let resource = Box::from(ASTTy::from((resource, finished)));
Expand Down
2 changes: 1 addition & 1 deletion src/check/constrain/generate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub fn generate(
AnonFun { .. } => gen_expr(ast, env, ctx, constr),
Pass => gen_expr(ast, env, ctx, constr),

With { .. } => gen_resources(ast, env, ctx, constr),
Using { .. } => gen_resources(ast, env, ctx, constr),

SetBuilder { .. } | ListBuilder { .. } | DictBuilder { .. } => {
gen_coll(ast, env, ctx, constr)
Expand Down
12 changes: 6 additions & 6 deletions src/check/constrain/generate/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ pub fn gen_resources(
constr: &mut ConstrBuilder,
) -> Constrained {
match &ast.node {
Node::With {
Node::Using {
resource,
alias: Some((alias, mutable, ty)),
expr,
} => {
constr.add(
"with alias",
"using alias",
&Expected::from(resource),
&Expected::from(alias),
env,
);
constr.add(
"with resource",
"using resource",
&Expected::from(resource),
&Expected::any(resource.pos),
env,
Expand All @@ -41,7 +41,7 @@ pub fn gen_resources(
name: Name::try_from(ty)?,
};
constr.add(
"with alias type",
"using alias type",
&Expected::from(resource),
&Expected::new(ty.pos, &ty_exp),
env,
Expand Down Expand Up @@ -71,9 +71,9 @@ pub fn gen_resources(

Ok(env.clone())
}
Node::With { resource, expr, .. } => {
Node::Using { resource, expr, .. } => {
constr.add(
"with",
"using",
&Expected::from(resource),
&Expected::any(resource.pos),
env,
Expand Down
2 changes: 1 addition & 1 deletion src/parse/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub enum Node {
expr_or_stmt: Box<AST>,
cases: Vec<AST>,
},
With {
Using {
resource: Box<AST>,
alias: Option<(Box<AST>, bool, Option<Box<AST>>)>,
expr: Box<AST>,
Expand Down
16 changes: 8 additions & 8 deletions src/parse/ast/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl Display for Node {
Node::AnonFun { .. } => String::from("anonymous function"),
Node::Raise { .. } => String::from("raise"),
Node::Handle { .. } => String::from("handle"),
Node::With { .. } => String::from("with"),
Node::Using { .. } => String::from("using"),
Node::FunctionCall { name, args } => {
format!(
"{}({})",
Expand Down Expand Up @@ -240,11 +240,11 @@ impl Node {
expr_or_stmt: Box::from(expr_or_stmt.map(mapping)),
cases: cases.iter().map(|c| c.map(mapping)).collect(),
},
Node::With {
Node::Using {
resource,
alias,
expr,
} => Node::With {
} => Node::Using {
resource: Box::from(resource.map(mapping)),
alias: alias.map(|(resource, alias, expr)| {
(
Expand Down Expand Up @@ -561,12 +561,12 @@ impl Node {
},
) => les.same_value(res) && equal_vec(lc, rc),
(
Node::With {
Node::Using {
resource: lr,
alias: Some((la, lmut, lty)),
expr: le,
},
Node::With {
Node::Using {
resource: rr,
alias: Some((ra, rmut, rty)),
expr: re,
Expand All @@ -579,12 +579,12 @@ impl Node {
&& le.same_value(re)
}
(
Node::With {
Node::Using {
resource: lr,
alias: None,
expr: le,
},
Node::With {
Node::Using {
resource: rr,
alias: None,
expr: re,
Expand Down Expand Up @@ -1382,7 +1382,7 @@ mod test {
cases: vec![*first.clone()],
expr_or_stmt: second.clone()
});
two_ast!(Node::With {
two_ast!(Node::Using {
resource: first.clone(),
alias: Some((second.clone(), false, Some(third.clone()))),
expr: Box::from(AST::new(Position::invisible(), Node::Pass))
Expand Down
6 changes: 3 additions & 3 deletions src/parse/control_flow_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fn parse_match(it: &mut LexIterator) -> ParseResult {
it.eat(&Token::Match, "match")?;
let cond = it.parse(&parse_expression, "match", start)?;
it.eat_while(&Token::NL);
it.eat(&Token::With, "match")?;
it.eat(&Token::Where, "match")?;
it.eat_while(&Token::NL);
let cases = it.parse_vec(&parse_match_cases, "match", start)?;
let end = cases.last().cloned().map_or(cond.pos, |case| case.pos);
Expand Down Expand Up @@ -145,7 +145,7 @@ mod test {

#[test]
fn match_verify() {
let source = String::from("match a with\n a => b\n c => d\nend");
let source = String::from("match a where\n a => b\n c => d\nend");
let statements = parse_direct(&source).unwrap();

let Node::Match { cond, cases } = &statements.first().expect("script empty.").node else {
Expand Down Expand Up @@ -285,7 +285,7 @@ mod test {

#[test]
fn match_missing_arms() {
let source = String::from("match a with\n ");
let source = String::from("match a where\n ");
parse_direct(&source).unwrap_err();
}

Expand Down
4 changes: 2 additions & 2 deletions src/parse/lex/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub enum Token {
Continue,
Break,
Ret,
With,
Using,

Question,

Expand Down Expand Up @@ -235,7 +235,7 @@ impl fmt::Display for Token {
Token::Break => write!(f, "break"),
Token::Ret => write!(f, "return"),
Token::Do => write!(f, "do"),
Token::With => write!(f, "with"),
Token::Using => write!(f, "using"),

Token::Question => write!(f, "?"),

Expand Down
2 changes: 1 addition & 1 deletion src/parse/lex/tokenize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ fn as_op_or_id(string: String) -> Token {
"return" => Token::Ret,
"then" => Token::Then,
"do" => Token::Do,
"with" => Token::With,
"using" => Token::Using,

"in" => Token::In,
"when" => Token::When,
Expand Down
Loading
Loading