From d0f23e60e950489dda129b26c2126c078e1e42e5 Mon Sep 17 00:00:00 2001 From: Ryan Daum Date: Sun, 16 Feb 2025 11:38:13 -0500 Subject: [PATCH] Allow return as expression, allows short-circuit conditionals In Julia and some other languages, the following is allowed: true && return; player == this && return true; etc Which is a nice abbreviated idiom to avoid large chains of if blocks This is possible because in those languages "return" is an expression not a statement. This is now the case for moor, too. This also fixes old broken string literal parsing code which returned E_INVARG instead of compiler error for invalid ints. --- crates/compiler/src/ast.rs | 12 ++- crates/compiler/src/codegen.rs | 14 +-- crates/compiler/src/codegen_tests.rs | 27 ++++- crates/compiler/src/decompile.rs | 6 +- crates/compiler/src/moo.pest | 7 +- crates/compiler/src/parse.rs | 110 ++++++++++++-------- crates/compiler/src/unparse.rs | 18 ++-- crates/kernel/src/builtins/bf_properties.rs | 2 +- crates/kernel/src/builtins/bf_server.rs | 2 +- crates/kernel/src/builtins/bf_verbs.rs | 24 +---- crates/kernel/src/vm/vm_test.rs | 2 + 11 files changed, 133 insertions(+), 91 deletions(-) diff --git a/crates/compiler/src/ast.rs b/crates/compiler/src/ast.rs index 5192da35..d2c49eaa 100644 --- a/crates/compiler/src/ast.rs +++ b/crates/compiler/src/ast.rs @@ -182,6 +182,7 @@ pub enum Expr { from: Box, to: Box, }, + Return(Option>), } #[derive(Debug, Eq, PartialEq, Clone)] @@ -280,10 +281,18 @@ pub enum StmtNode { Continue { exit: Option, }, - Return(Option), Expr(Expr), } +impl StmtNode { + pub fn mk_return(expr: Expr) -> Self { + StmtNode::Expr(Expr::Return(Some(Box::new(expr)))) + } + pub fn mk_return_none() -> Self { + StmtNode::Expr(Expr::Return(None)) + } +} + // Recursive descent compare of two trees, ignoring the parser-provided line numbers, but // validating equality for everything else. #[cfg(test)] @@ -293,7 +302,6 @@ pub fn assert_trees_match_recursive(a: &[Stmt], b: &[Stmt]) { assert_eq!(left.tree_line_no, right.tree_line_no); match (&left.node, &right.node) { - (StmtNode::Return(_), StmtNode::Return(_)) => {} (StmtNode::Expr(e1), StmtNode::Expr(e2)) => { assert_eq!(e1, e2); } diff --git a/crates/compiler/src/codegen.rs b/crates/compiler/src/codegen.rs index eddc8369..6324ecff 100644 --- a/crates/compiler/src/codegen.rs +++ b/crates/compiler/src/codegen.rs @@ -602,6 +602,14 @@ impl CodegenState { }); self.commit_jump_label(end_label); } + Expr::Return(Some(expr)) => { + self.generate_expr(expr)?; + self.emit(Op::Return); + } + Expr::Return(None) => { + self.emit(Op::Return0); + self.push_stack(1); + } } Ok(()) @@ -902,12 +910,6 @@ impl CodegenState { let l = self.find_loop(&l).expect("invalid loop for break/continue"); self.emit(Op::ExitId(l.top_label)); } - StmtNode::Return(Some(expr)) => { - self.generate_expr(expr)?; - self.emit(Op::Return); - self.pop_stack(1); - } - StmtNode::Return(None) => self.emit(Op::Return0), StmtNode::Expr(e) => { self.generate_expr(e)?; self.emit(Op::Pop); diff --git a/crates/compiler/src/codegen_tests.rs b/crates/compiler/src/codegen_tests.rs index 1fa2cc02..dad42ce1 100644 --- a/crates/compiler/src/codegen_tests.rs +++ b/crates/compiler/src/codegen_tests.rs @@ -73,6 +73,7 @@ mod tests { Pop, Push(a), Return, + Pop, Done ] ); @@ -111,6 +112,7 @@ mod tests { If(1.into(), 0), ImmInt(5), Return, + Pop, EndScope { num_bindings: 0 }, Jump { label: 0.into() }, ImmInt(2), @@ -119,6 +121,7 @@ mod tests { Eif(2.into(), 0), ImmInt(3), Return, + Pop, EndScope { num_bindings: 0 }, Jump { label: 0.into() }, BeginScope { @@ -127,6 +130,7 @@ mod tests { }, ImmInt(6), Return, + Pop, EndScope { num_bindings: 0 }, Done ] @@ -594,7 +598,7 @@ mod tests { let binary = compile(program, CompileOptions::default()).unwrap(); assert_eq!( *binary.main_vector.as_ref(), - vec![Imm(0.into()), ImmInt(1), Ref, Return, Done] + vec![Imm(0.into()), ImmInt(1), Ref, Return, Pop, Done] ); } @@ -604,7 +608,15 @@ mod tests { let binary = compile(program, CompileOptions::default()).unwrap(); assert_eq!( *binary.main_vector.as_ref(), - vec![Imm(0.into()), ImmInt(1), ImmInt(2), RangeRef, Return, Done] + vec![ + Imm(0.into()), + ImmInt(1), + ImmInt(2), + RangeRef, + Return, + Pop, + Done + ] ); } @@ -671,6 +683,7 @@ mod tests { ImmInt(1), Ref, Return, + Pop, Done ] ); @@ -693,6 +706,7 @@ mod tests { ImmInt(2), RangeRef, Return, + Pop, Done ] ); @@ -770,6 +784,7 @@ mod tests { RangeRef, CheckListForSplice, Return, + Pop, Done ] ); @@ -961,6 +976,7 @@ mod tests { ImmInt(1), Ref, Return, + Pop, Done ] ) @@ -1258,6 +1274,7 @@ mod tests { Push(c), ListAddTail, Return, + Pop, Done ] ) @@ -1367,6 +1384,7 @@ mod tests { Imm(binary.find_literal("stack".into())), GetProp, Return, + Pop, Done ] ) @@ -1393,7 +1411,7 @@ mod tests { fn test_0_arg_return() { let program = r#"return;"#; let binary = compile(program, CompileOptions::default()).unwrap(); - assert_eq!(*binary.main_vector.as_ref(), vec![Return0, Done]) + assert_eq!(*binary.main_vector.as_ref(), vec![Return0, Pop, Done]) } #[test] @@ -1438,6 +1456,7 @@ mod tests { Pop, Push(pass), Return, + Pop, Done ] ) @@ -1487,6 +1506,7 @@ mod tests { Length(Offset(0)), RangeRef, Return, + Pop, EndExcept(Label(1)), Pop, Done @@ -1551,6 +1571,7 @@ mod tests { RangeRef, MapInsert, Return, + Pop, Done ] ); diff --git a/crates/compiler/src/decompile.rs b/crates/compiler/src/decompile.rs index 476a2449..c3e92a98 100644 --- a/crates/compiler/src/decompile.rs +++ b/crates/compiler/src/decompile.rs @@ -428,12 +428,10 @@ impl Decompile { } Op::Return => { let expr = self.pop_expr()?; - self.statements - .push(Stmt::new(StmtNode::Return(Some(expr)), line_num)); + self.push_expr(Expr::Return(Some(Box::new(expr)))); } Op::Return0 => { - self.statements - .push(Stmt::new(StmtNode::Return(None), line_num)); + self.push_expr(Expr::Return(None)); } Op::Done => { let opcode_vector = &self.opcode_vector(); diff --git a/crates/compiler/src/moo.pest b/crates/compiler/src/moo.pest index e9022b00..7f0bd7af 100644 --- a/crates/compiler/src/moo.pest +++ b/crates/compiler/src/moo.pest @@ -22,7 +22,7 @@ statement = { | labelled_fork_statement | break_statement | continue_statement - | return_statement + | empty_return | try_except_statement | try_finally_statement | begin_statement @@ -41,6 +41,8 @@ for_statement = { ^"for" ~ ident ~ "in" ~ (for_range_clause | for_in_clause) for_range_clause = { "[" ~ expr ~ ".." ~ expr ~ "]" } for_in_clause = { "(" ~ expr ~ ")" } +empty_return = { ^"return" ~ ";"} + labelled_while_statement = { ^"while" ~ ident ~ "(" ~ expr ~ ")" ~ statements ~ ^"endwhile" } while_statement = { ^"while" ~ "(" ~ expr ~ ")" ~ statements ~ ^"endwhile" } @@ -75,7 +77,6 @@ global_assignment = { ^"global" ~ ident ~ (ASSIGN ~ expr)? ~ ";" } codes = { anycode | exprlist } anycode = { ^"any" } -return_statement = { ^"return" ~ (expr)? ~ ";" } expr_statement = { (expr)? ~ ";" } expr = { (integer | (prefix* ~ primary)) ~ postfix* ~ (infix ~ (integer | (prefix* ~ primary)) ~ postfix*)* } @@ -135,9 +136,11 @@ prop = { "." ~ ident } prop_expr = { "." ~ "(" ~ expr ~ ")" } assign = { "=" ~ expr } cond_expr = { "?" ~ expr ~ "|" ~ expr } +return_expr = { "return" ~ (expr)? } primary = _{ pass_expr + | return_expr | builtin_call | paren_expr | sysprop_call diff --git a/crates/compiler/src/parse.rs b/crates/compiler/src/parse.rs index 36ab2462..5ba8ce66 100644 --- a/crates/compiler/src/parse.rs +++ b/crates/compiler/src/parse.rs @@ -25,7 +25,6 @@ use pest::error::LineColLocation; use pest::iterators::Pairs; use pest::pratt_parser::{Assoc, Op, PrattParser}; pub use pest::Parser as PestParser; -use tracing::warn; use moor_values::Error::{ E_ARGS, E_DIV, E_FLOAT, E_INVARG, E_INVIND, E_MAXREC, E_NACC, E_NONE, E_PERM, E_PROPNF, @@ -118,10 +117,10 @@ impl TreeTransformer { } Rule::integer => match pairs.as_str().parse::() { Ok(int) => Ok(Expr::Value(v_int(int))), - Err(e) => { - warn!("Failed to parse '{}' to i64: {}", pairs.as_str(), e); - Ok(Expr::Value(v_err(E_INVARG))) - } + Err(e) => Err(CompileError::StringLexError(format!( + "invalid integer literal '{}': {e}", + pairs.as_str() + ))), }, Rule::boolean => { if !self.options.bool_type { @@ -241,7 +240,7 @@ impl TreeTransformer { // Generally following C-like precedence order as described: // https://en.cppreference.com/w/c/language/operator_precedence // Precedence from lowest to highest. - // 14. Assignments are lowest precedence. + // 14. Assignments & returns are lowest precedence. .op(Op::postfix(Rule::assign) | Op::prefix(Rule::scatter_assign)) // 13. Ternary conditional .op(Op::postfix(Rule::cond_expr)) @@ -445,8 +444,10 @@ impl TreeTransformer { Rule::integer => match primary.as_str().parse::() { Ok(int) => Ok(Expr::Value(v_int(int))), Err(e) => { - warn!("Failed to parse '{}' to i64: {}", primary.as_str(), e); - Ok(Expr::Value(v_err(E_INVARG))) + return Err(CompileError::StringLexError(format!( + "invalid integer literal '{}': {e}", + primary.as_str() + ))); } }, Rule::range_comprehension => { @@ -509,6 +510,15 @@ impl TreeTransformer { } } } + Rule::return_expr => { + let mut inner = primary.into_inner(); + let rhs = match inner.next() { + Some(e) => Some(Box::new(self.clone().parse_expr(e.into_inner())?)), + None => None, + }; + Ok(Expr::Return(rhs)) + } + _ => todo!("Unimplemented primary: {:?}", primary.as_rule()), }) .map_infix(|lhs, op, rhs| match op.as_rule() { @@ -844,13 +854,13 @@ impl TreeTransformer { }; Ok(Some(Stmt::new(StmtNode::Continue { exit: label }, line))) } - Rule::return_statement => { - let mut parts = pair.into_inner(); - let expr = parts - .next() - .map(|expr| self.parse_expr(expr.into_inner()).unwrap()); - Ok(Some(Stmt::new(StmtNode::Return(expr), line))) - } + // Rule::return_statement => { + // let mut parts = pair.into_inner(); + // let expr = parts + // .next() + // .map(|expr| self.parse_expr(expr.into_inner()).unwrap()); + // Ok(Some(Stmt::new(StmtNode::Return(expr), line))) + // } Rule::for_statement => { self.enter_scope(); let mut parts = pair.into_inner(); @@ -1093,6 +1103,7 @@ impl TreeTransformer { line, ))) } + Rule::empty_return => Ok(Some(Stmt::new(StmtNode::mk_return_none(), line))), _ => panic!("Unimplemented statement: {:?}", pair.as_rule()), } } @@ -1379,7 +1390,7 @@ pub fn unquote_str(s: &str) -> Result { #[cfg(test)] mod tests { use moor_values::Error::{E_INVARG, E_PROPNF, E_VARNF}; - use moor_values::{v_err, v_float, v_int, v_objid, v_str}; + use moor_values::{v_err, v_float, v_int, v_objid, v_str, Var}; use moor_values::{v_none, Symbol}; use crate::ast::Arg::{Normal, Splice}; @@ -1416,7 +1427,9 @@ mod tests { assert_eq!(parse.stmts.len(), 1); assert_eq!( stripped_stmts(&parse.stmts), - vec![StmtNode::Return(Some(Value(v_float(1e-9))))] + vec![StmtNode::Expr(Expr::Return(Some(Box::new(Value( + v_float(1e-9) + )))))] ); } @@ -1491,7 +1504,7 @@ mod tests { Box::new(Value(v_int(2))), ), statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(5)))), + node: StmtNode::mk_return(Value(v_int(5))), parser_line_no: 1, tree_line_no: 2, }], @@ -1505,7 +1518,7 @@ mod tests { Box::new(Value(v_int(3))), ), statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(3)))), + node: StmtNode::mk_return(Value(v_int(3))), parser_line_no: 1, tree_line_no: 4, }], @@ -1514,7 +1527,7 @@ mod tests { otherwise: Some(ElseArm { statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(6)))), + node: StmtNode::mk_return(Value(v_int(6))), parser_line_no: 1, tree_line_no: 6, }], @@ -1551,7 +1564,7 @@ mod tests { Box::new(Value(v_int(2))), ), statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(5)))), + node: StmtNode::mk_return(Value(v_int(5))), parser_line_no: 3, tree_line_no: 2, }], @@ -1564,7 +1577,7 @@ mod tests { Box::new(Value(v_int(3))), ), statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(3)))), + node: StmtNode::mk_return(Value(v_int(3))), parser_line_no: 5, tree_line_no: 4, }], @@ -1577,7 +1590,7 @@ mod tests { Box::new(Value(v_int(4))), ), statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(4)))), + node: StmtNode::mk_return(Value(v_int(4))), parser_line_no: 7, tree_line_no: 6, }], @@ -1586,7 +1599,7 @@ mod tests { otherwise: Some(ElseArm { statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(6)))), + node: StmtNode::mk_return(Value(v_int(6))), parser_line_no: 9, tree_line_no: 8, }], @@ -1603,14 +1616,14 @@ mod tests { assert_eq!(parse.stmts.len(), 1); assert_eq!( stripped_stmts(&parse.stmts)[0], - StmtNode::Return(Some(Expr::Unary( + StmtNode::mk_return(Expr::Unary( UnaryOp::Not, Box::new(Verb { location: Box::new(Value(v_objid(2))), verb: Box::new(Value(v_str("move"))), args: vec![Normal(Value(v_int(5)))], }) - ))) + )) ); } @@ -1641,7 +1654,7 @@ mod tests { }) ), statements: vec![Stmt { - node: StmtNode::Return(None), + node: StmtNode::mk_return_none(), parser_line_no: 3, tree_line_no: 2, }], @@ -1990,7 +2003,7 @@ mod tests { ]), body: vec![], }, - StmtNode::Return(Some(Id(i))), + StmtNode::mk_return(Id(i)), ] ) } @@ -2048,13 +2061,13 @@ mod tests { let args = parse.unbound_names.find_name("args").unwrap(); assert_eq!( stripped_stmts(&parse.stmts), - vec![StmtNode::Return(Some(Expr::List(vec![ + vec![StmtNode::mk_return(Expr::List(vec![ Splice(Id(results)), Normal(Call { function: Symbol::mk("frozzbozz"), args: vec![Splice(Id(args))], }), - ])))] + ]))] ); } @@ -2141,14 +2154,14 @@ mod tests { Box::new(Value(v_int(5))), ), statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(5)))), + node: StmtNode::mk_return(Value(v_int(5))), parser_line_no: 2, tree_line_no: 2, }], }], otherwise: Some(ElseArm { statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(3)))), + node: StmtNode::mk_return(Value(v_int(3))), parser_line_no: 4, tree_line_no: 4, }], @@ -2183,7 +2196,7 @@ mod tests { Box::new(Value(v_int(5))), ), statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(5)))), + node: StmtNode::mk_return(Value(v_int(5))), parser_line_no: 2, tree_line_no: 2, }], @@ -2196,7 +2209,7 @@ mod tests { Box::new(Value(v_int(2))), ), statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(2)))), + node: StmtNode::mk_return(Value(v_int(2))), parser_line_no: 4, tree_line_no: 4, }], @@ -2204,7 +2217,7 @@ mod tests { ], otherwise: Some(ElseArm { statements: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(3)))), + node: StmtNode::mk_return(Value(v_int(3))), parser_line_no: 6, tree_line_no: 6, }], @@ -2263,7 +2276,7 @@ mod tests { id: None, codes: CatchCodes::Codes(vec![Normal(Value(v_err(E_PROPNF)))]), statements: vec![Stmt { - node: StmtNode::Return(None), + node: StmtNode::mk_return_none(), parser_line_no: 4, tree_line_no: 4, }], @@ -2466,13 +2479,13 @@ mod tests { let varnf = Normal(Value(v_err(E_VARNF))); assert_eq!( stripped_stmts(&parse.stmts), - vec![StmtNode::Return(Some(Expr::List(vec![Normal( + vec![StmtNode::mk_return(Expr::List(vec![Normal( Expr::TryCatch { trye: Box::new(Id(parse.unbound_names.find_name("x").unwrap())), codes: CatchCodes::Codes(vec![varnf]), except: Some(Box::new(Value(v_int(666)))), } - )],)))] + )],))] ) } @@ -2587,7 +2600,7 @@ mod tests { left: Box::new(Id(parse.unbound_names.find_name("pass").unwrap())), right: Box::new(Id(parse.unbound_names.find_name("blop").unwrap())), }), - StmtNode::Return(Some(Id(parse.unbound_names.find_name("pass").unwrap()))), + StmtNode::mk_return(Id(parse.unbound_names.find_name("pass").unwrap())), ] ); } @@ -2622,7 +2635,7 @@ mod tests { vec![StmtNode::Scope { num_bindings: 0, body: vec![Stmt { - node: StmtNode::Return(Some(Value(v_int(5)))), + node: StmtNode::mk_return(Value(v_int(5))), parser_line_no: 2, tree_line_no: 2, }], @@ -2721,7 +2734,7 @@ mod tests { }, ], }, - StmtNode::Return(Some(Id(global_x))) + StmtNode::mk_return(Id(global_x)) ] ); } @@ -2998,4 +3011,19 @@ mod tests { })] ) } + + /// Modification to the MOO syntax which allows "return" to be an expression so as to allow + /// the following syntax, as in Julia.... + #[test] + fn test_return_as_expr() { + let program = r#"true && return 5;"#; + let parse = parse_program(program, CompileOptions::default()).unwrap(); + assert_eq!( + stripped_stmts(&parse.stmts), + vec![StmtNode::Expr(Expr::And( + Box::new(Value(Var::mk_bool(true))), + Box::new(Expr::Return(Some(Box::new(Value(v_int(5)))))) + ))] + ); + } } diff --git a/crates/compiler/src/unparse.rs b/crates/compiler/src/unparse.rs index 124785b9..7ca629e3 100644 --- a/crates/compiler/src/unparse.rs +++ b/crates/compiler/src/unparse.rs @@ -77,6 +77,7 @@ impl Expr { Expr::Pass { .. } => 1, Expr::Call { .. } => 1, Expr::Length => 1, + Expr::Return(_) => 1, Expr::TryCatch { .. } => 1, }; 15 - cpp_ref_prep @@ -261,6 +262,10 @@ impl<'a> Unparse<'a> { buffer.push('\''); Ok(buffer) } + Expr::Return(expr) => Ok(match expr { + None => "return".to_string(), + Some(e) => format!("return {}", self.unparse_expr(e)?), + }), Expr::Index(lvalue, index) => { let left = brace_if_lower(lvalue); let right = self.unparse_expr(index).unwrap(); @@ -637,14 +642,6 @@ impl<'a> Unparse<'a> { base_str.push(';'); Ok(vec![base_str]) } - StmtNode::Return(expr) => Ok(match expr { - None => { - vec![format!("{}return;", indent_frag)] - } - Some(e) => { - vec![format!("{}return {};", indent_frag, self.unparse_expr(e)?)] - } - }), StmtNode::Expr(Expr::Assign { left, right }) => { let left_frag = match left.as_ref() { Expr::Id(id) => { @@ -750,10 +747,7 @@ pub fn annotate_line_numbers(start_line_no: usize, tree: &mut [Stmt]) -> usize { // ENDFOR/ENDWHILE/ENDFORK line_no += 1; } - StmtNode::Expr(_) - | StmtNode::Break { .. } - | StmtNode::Continue { .. } - | StmtNode::Return(_) => { + StmtNode::Expr(_) | StmtNode::Break { .. } | StmtNode::Continue { .. } => { // All single-line statements. line_no += 1; } diff --git a/crates/kernel/src/builtins/bf_properties.rs b/crates/kernel/src/builtins/bf_properties.rs index b6c10d2c..67864a11 100644 --- a/crates/kernel/src/builtins/bf_properties.rs +++ b/crates/kernel/src/builtins/bf_properties.rs @@ -15,10 +15,10 @@ use moor_compiler::offset_for_builtin; use moor_values::model::{prop_flags_string, PropAttrs, PropFlag}; use moor_values::util::BitEnum; use moor_values::Error::{E_ARGS, E_INVARG, E_TYPE}; +use moor_values::Sequence; use moor_values::Variant; use moor_values::{v_empty_list, List}; use moor_values::{v_list, v_none, v_obj, v_string}; -use moor_values::Sequence; use crate::bf_declare; use crate::builtins::BfErr::Code; diff --git a/crates/kernel/src/builtins/bf_server.rs b/crates/kernel/src/builtins/bf_server.rs index f9165d89..ffedcfca 100644 --- a/crates/kernel/src/builtins/bf_server.rs +++ b/crates/kernel/src/builtins/bf_server.rs @@ -32,11 +32,11 @@ use moor_values::tasks::Event::{Present, Unpresent}; use moor_values::tasks::TaskId; use moor_values::tasks::{NarrativeEvent, Presentation}; use moor_values::Error::{E_ARGS, E_INVARG, E_INVIND, E_PERM, E_TYPE}; +use moor_values::Sequence; use moor_values::VarType::TYPE_STR; use moor_values::Variant; use moor_values::{v_int, v_list, v_none, v_obj, v_str, v_string, Var}; use moor_values::{v_list_iter, Error}; -use moor_values::Sequence; fn bf_noop(bf_args: &mut BfCallState<'_>) -> Result { error!( diff --git a/crates/kernel/src/builtins/bf_verbs.rs b/crates/kernel/src/builtins/bf_verbs.rs index f8d4582e..679475fb 100644 --- a/crates/kernel/src/builtins/bf_verbs.rs +++ b/crates/kernel/src/builtins/bf_verbs.rs @@ -71,11 +71,7 @@ fn bf_verb_info(bf_args: &mut BfCallState<'_>) -> Result { bf_args .world_state - .get_verb( - &bf_args.task_perms_who(), - obj, - verb_name, - ) + .get_verb(&bf_args.task_perms_who(), obj, verb_name) .map_err(world_state_bf_err)? } }; @@ -107,7 +103,7 @@ fn get_verbdef(obj: &Obj, verbspec: Var, bf_args: &BfCallState<'_>) -> Result { let Ok(verb_desc) = verbspec.as_symbol() else { - return Err(BfErr::Code(E_TYPE)) + return Err(BfErr::Code(E_TYPE)); }; bf_args @@ -200,16 +196,11 @@ fn bf_set_verb_info(bf_args: &mut BfCallState<'_>) -> Result { } _ => { let Ok(verb_name) = bf_args.args[1].as_symbol() else { - return Err(BfErr::Code(E_TYPE)) + return Err(BfErr::Code(E_TYPE)); }; bf_args .world_state - .update_verb( - &bf_args.task_perms_who(), - obj, - verb_name, - update_attrs, - ) + .update_verb(&bf_args.task_perms_who(), obj, verb_name, update_attrs) .map_err(world_state_bf_err)?; } } @@ -311,12 +302,7 @@ fn bf_set_verb_args(bf_args: &mut BfCallState<'_>) -> Result { }; bf_args .world_state - .update_verb( - &bf_args.task_perms_who(), - obj, - verb_name, - update_attrs, - ) + .update_verb(&bf_args.task_perms_who(), obj, verb_name, update_attrs) .map_err(world_state_bf_err)?; } } diff --git a/crates/kernel/src/vm/vm_test.rs b/crates/kernel/src/vm/vm_test.rs index 5a94f828..8e078854 100644 --- a/crates/kernel/src/vm/vm_test.rs +++ b/crates/kernel/src/vm/vm_test.rs @@ -1137,6 +1137,8 @@ mod tests { v_map(&[(v_int(1),v_int(2)), (v_int(2),v_int(3),), (v_int(3),v_int(4))]); "map range to end")] #[test_case("l = {1,2,3}; l[2..3] = {6, 7, 8, 9}; return l;", v_list(&[v_int(1), v_int(6), v_int(7), v_int(8), v_int(9)]); "list assignment to range")] + #[test_case("1 == 2 && return 0; 1 == 1 && return 1; return 2;", v_int(1); "short circuit return expr")] + #[test_case("true && return;", v_int(0); "short circuit empty return expr")] fn test_run(program: &str, expected_result: Var) { let mut state = world_with_test_program(program); let session = Arc::new(NoopClientSession::new());