use super::*;
#[test]
fn parses_ternary_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/ternary-basic.mel"
));
assert!(parse.errors.is_empty());
assert_eq!(parse.syntax.items.len(), 3);
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => assert!(matches!(**rhs, Expr::Ternary { .. })),
_ => panic!("expected ternary assignment"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_exponent_float_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/exponent-float-basic.mel"
));
assert!(parse.errors.is_empty());
assert_eq!(parse.syntax.items.len(), 3);
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => assert!(matches!(
**rhs,
Expr::Float { ref text, .. } if parse.source_slice(*text) == "1.0e-3"
)),
_ => panic!("expected exponent assignment"),
},
_ => panic!("expected statement"),
}
match &parse.syntax.items[1] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => match &**rhs {
Expr::Binary { lhs, rhs, .. } => {
assert!(matches!(
**lhs,
Expr::Float { ref text, .. } if parse.source_slice(*text) == "1e+3"
));
assert!(matches!(
**rhs,
Expr::Float { ref text, .. } if parse.source_slice(*text) == "0.0e0"
));
}
_ => panic!("expected exponent binary expression"),
},
_ => panic!("expected assignment"),
},
_ => panic!("expected statement"),
}
match &parse.syntax.items[2] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => {
assert!(matches!(
decl.declarators[0].initializer,
Some(Expr::ArrayLiteral { .. })
));
}
_ => panic!("expected variable declaration"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_trailing_dot_float_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/trailing-dot-float-basic.mel"
));
assert!(parse.errors.is_empty());
assert_eq!(parse.syntax.items.len(), 3);
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => assert!(matches!(
**rhs,
Expr::Float { ref text, .. } if parse.source_slice(*text) == "1000."
)),
_ => panic!("expected trailing-dot float assignment"),
},
_ => panic!("expected statement"),
}
match &parse.syntax.items[1] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => match &**rhs {
Expr::Binary { lhs, rhs, .. } => {
assert!(matches!(
**lhs,
Expr::Float { ref text, .. } if parse.source_slice(*text) == "0."
));
assert!(matches!(
**rhs,
Expr::Float { ref text, .. } if parse.source_slice(*text) == "1."
));
}
_ => panic!("expected binary expression"),
},
_ => panic!("expected assignment"),
},
_ => panic!("expected statement"),
}
match &parse.syntax.items[2] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => match &**rhs {
Expr::ArrayLiteral { elements, .. } => {
assert_eq!(elements.len(), 3);
assert!(matches!(
elements[0],
Expr::Float { ref text, .. } if parse.source_slice(*text) == "0."
));
assert!(matches!(
elements[1],
Expr::Float { ref text, .. } if parse.source_slice(*text) == "1."
));
assert!(matches!(
elements[2],
Expr::Float { ref text, .. } if parse.source_slice(*text) == "2."
));
}
_ => panic!("expected brace-list assignment"),
},
_ => panic!("expected assignment"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_for_in_and_for_loop_fixtures() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/while-basic.mel"
));
assert!(parse.errors.is_empty());
assert!(matches!(
parse.syntax.items[0],
Item::Stmt(ref stmt) if matches!(&**stmt, Stmt::While { .. })
));
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/for-loop-basic.mel"
));
assert!(parse.errors.is_empty());
assert!(matches!(
parse.syntax.items[0],
Item::Stmt(ref stmt) if matches!(&**stmt, Stmt::For { .. })
));
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/for-loop-multi-init-update.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::For {
init: Some(init),
condition: Some(_),
update: Some(update),
..
} => {
assert_eq!(init.len(), 2);
assert_eq!(update.len(), 2);
}
_ => panic!("expected classic for statement with multi-clause init/update"),
},
_ => panic!("expected statement"),
}
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/for-in-basic.mel"
));
assert!(parse.errors.is_empty());
assert!(matches!(
parse.syntax.items[0],
Item::Stmt(ref stmt) if matches!(&**stmt, Stmt::ForIn { .. })
));
}
#[test]
fn parses_for_in_and_for_with_shared_prefix_tokens() {
let parse = parse_source(
"for ($item in some_array) {\n print $item;\n}\nfor ($i = 0; $i < 3; ++$i) {\n print $i;\n}\n",
);
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::ForIn { binding, .. } => match &**binding {
Expr::Ident {
name_range: _,
range: _,
} => {}
_ => panic!("expected variable binding"),
},
_ => panic!("expected for-in statement"),
},
_ => panic!("expected statement"),
}
match &parse.syntax.items[1] {
Item::Stmt(stmt) => match &**stmt {
Stmt::For {
init,
condition,
update,
..
} => {
assert!(init.is_some());
assert!(condition.is_some());
assert!(update.is_some());
}
_ => panic!("expected classic for statement"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_if_else_and_break_continue_fixtures() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/if-else-command.mel"
));
assert!(parse.errors.is_empty());
assert!(matches!(
parse.syntax.items[0],
Item::Stmt(ref stmt) if matches!(&**stmt, Stmt::If { .. })
));
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/break-continue.mel"
));
assert!(parse.errors.is_empty());
}
#[test]
fn parses_switch_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/switch-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Switch { clauses, .. } => {
assert_eq!(clauses.len(), 3);
assert!(matches!(clauses[0].label, SwitchLabel::Default { .. }));
assert!(clauses[0].statements.len() == 2);
assert!(matches!(clauses[1].label, SwitchLabel::Case(_)));
assert!(clauses[1].statements.is_empty());
assert!(matches!(clauses[2].label, SwitchLabel::Case(_)));
assert!(clauses[2].statements.len() == 2);
}
_ => panic!("expected switch statement"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_postfix_update_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/postfix-update.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr: Expr::PostfixUpdate { op, .. },
..
} => assert!(matches!(op, UpdateOp::Increment)),
_ => panic!("expected postfix update"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_compound_assign_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/compound-assign-basic.mel"
));
assert!(parse.errors.is_empty());
assert_eq!(parse.syntax.items.len(), 3);
let expected = [
AssignOp::SubAssign,
AssignOp::MulAssign,
AssignOp::DivAssign,
];
for (item, expected_op) in parse.syntax.items.iter().zip(expected) {
match item {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr: Expr::Assign { op, .. },
..
} => assert_eq!(*op, expected_op),
_ => panic!("expected compound assignment"),
},
_ => panic!("expected statement"),
}
}
}
#[test]
fn parses_prefix_update_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/prefix-update-basic.mel"
));
assert!(parse.errors.is_empty());
assert_eq!(parse.syntax.items.len(), 2);
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr: Expr::PrefixUpdate { op, .. },
..
} => assert!(matches!(op, UpdateOp::Increment)),
_ => panic!("expected prefix increment"),
},
_ => panic!("expected statement"),
}
match &parse.syntax.items[1] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr: Expr::PrefixUpdate { op, .. },
..
} => assert!(matches!(op, UpdateOp::Decrement)),
_ => panic!("expected prefix decrement"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_do_while_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/do-while-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::DoWhile { body, .. } => {
assert!(matches!(&**body, Stmt::Block { .. }));
}
_ => panic!("expected do-while statement"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_variable_declaration_fixtures() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/var-decl-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => {
assert!(matches!(decl.ty, TypeName::Int));
assert_eq!(decl.declarators.len(), 1);
assert_eq!(parse.source_slice(decl.declarators[0].name_range), "$count");
}
_ => panic!("expected variable declaration"),
},
_ => panic!("expected statement"),
}
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/global-var-decl.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => {
assert!(decl.is_global);
assert!(matches!(decl.ty, TypeName::String));
assert!(decl.declarators[0].array_size.is_some());
assert!(matches!(
decl.declarators[0].initializer,
Some(Expr::ArrayLiteral { .. })
));
}
_ => panic!("expected global variable declaration"),
},
_ => panic!("expected statement"),
}
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/statements/var-decl-multi-array.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => {
assert_eq!(decl.declarators.len(), 2);
assert!(matches!(
decl.declarators[1].initializer,
Some(Expr::ArrayLiteral { .. })
));
}
_ => panic!("expected multi declarator"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_brace_list_assignment_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/brace-list-assign.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => {
assert!(matches!(**rhs, Expr::ArrayLiteral { .. }));
}
_ => panic!("expected assignment"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_cast_expression_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/cast-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => match decl.declarators[0].initializer.as_ref() {
Some(Expr::Cast { ty, expr, .. }) => {
assert!(matches!(ty, TypeName::String));
assert!(matches!(**expr, Expr::Ident { .. }));
}
_ => panic!("expected string cast initializer"),
},
_ => panic!("expected variable declaration"),
},
_ => panic!("expected statement"),
}
match &parse.syntax.items[1] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => match decl.declarators[0].initializer.as_ref() {
Some(Expr::Cast { ty, expr, .. }) => {
assert!(matches!(ty, TypeName::Int));
assert!(matches!(**expr, Expr::Binary { .. }));
}
_ => panic!("expected int cast initializer"),
},
_ => panic!("expected variable declaration"),
},
_ => panic!("expected statement"),
}
match &parse.syntax.items[2] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => match &**rhs {
Expr::Cast { ty, expr, .. } => {
assert!(matches!(ty, TypeName::String));
assert!(matches!(**expr, Expr::Invoke(_)));
}
_ => panic!("expected nested string cast"),
},
_ => panic!("expected assignment"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_path_like_bareword_expression_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/path-like-bareword-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => match decl.declarators[0].initializer.as_ref() {
Some(Expr::BareWord { text, .. }) => {
assert_eq!(parse.source_slice(*text), "AA_Bar*|mdl|_XXa0|");
}
_ => panic!("expected path-like bareword initializer"),
},
_ => panic!("expected variable declaration"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_hex_integer_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/hex-int-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => match &**rhs {
Expr::Binary { lhs, rhs, .. } => {
assert!(matches!(**lhs, Expr::Int { value, .. } if value == 0x8000));
assert!(matches!(**rhs, Expr::Int { value, .. } if value == 0x0001));
}
_ => panic!("expected hex integer binary expression"),
},
_ => panic!("expected assignment"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_caret_operator_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/caret-operator-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => match decl.declarators[0].initializer.as_ref() {
Some(Expr::Binary { op, .. }) => assert_eq!(op, &BinaryOp::Caret),
_ => panic!("expected caret binary expression"),
},
_ => panic!("expected variable declaration"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_two_element_vector_literal_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/vector-literal-two-elements.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::VarDecl { decl, .. } => match decl.declarators[0].initializer.as_ref() {
Some(Expr::VectorLiteral { elements, .. }) => assert_eq!(elements.len(), 2),
_ => panic!("expected vector literal initializer"),
},
_ => panic!("expected variable declaration"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_unary_negate_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/unary-negate-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => match &**rhs {
Expr::Binary { lhs, rhs, .. } => {
assert!(matches!(
**lhs,
Expr::Unary {
op: UnaryOp::Negate,
..
}
));
assert!(matches!(
**rhs,
Expr::Unary {
op: UnaryOp::Negate,
..
}
));
}
_ => panic!("expected binary negate expression"),
},
_ => panic!("expected assignment"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_vector_literal_and_component_fixtures() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/vector-literal-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => {
assert!(matches!(**rhs, Expr::VectorLiteral { .. }));
}
_ => panic!("expected vector assignment"),
},
_ => panic!("expected statement"),
}
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/vector-component-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => match &**rhs {
Expr::Binary { lhs, rhs, .. } => {
assert!(matches!(
**lhs,
Expr::ComponentAccess {
component: VectorComponent::X,
..
}
));
assert!(matches!(
**rhs,
Expr::ComponentAccess {
component: VectorComponent::Y,
..
}
));
}
_ => panic!("expected binary component access"),
},
_ => panic!("expected vector component assignment"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn parses_member_access_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/member-access-basic.mel"
));
assert!(parse.errors.is_empty());
match &parse.syntax.items[0] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => match &**rhs {
Expr::Binary { lhs, rhs, .. } => {
assert!(matches!(
**lhs,
Expr::MemberAccess { ref member, .. } if parse.source_slice(*member) == "foo"
));
assert!(matches!(
**rhs,
Expr::MemberAccess { ref member, .. } if parse.source_slice(*member) == "bar"
));
}
_ => panic!("expected binary member access"),
},
_ => panic!("expected member access assignment"),
},
_ => panic!("expected statement"),
}
match &parse.syntax.items[1] {
Item::Stmt(stmt) => match &**stmt {
Stmt::Expr {
expr:
Expr::Assign {
rhs,
op: AssignOp::Assign,
..
},
..
} => assert!(matches!(
**rhs,
Expr::MemberAccess { ref member, .. } if parse.source_slice(*member) == "name"
)),
_ => panic!("expected indexed member access"),
},
_ => panic!("expected statement"),
}
}
#[test]
fn reports_missing_index_bracket_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/missing-index-bracket.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected ']' after index expression"
);
}
#[test]
fn reports_missing_proc_body_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/proc/missing-proc-body.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected proc body block");
}
#[test]
fn reports_missing_proc_param_name_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/proc/missing-proc-param-name.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected '$' before proc parameter name"
);
}
#[test]
fn reports_missing_compound_assign_rhs_fixture() {
let parse = parse_source(include_str!(
"../../../../../tests/corpus/parser/expressions/missing-compound-assign-rhs.mel"
));
assert!(!parse.errors.is_empty());
assert_eq!(
parse.errors[0].message,
"expected expression after operator"
);
}
#[test]
fn parses_inline_path_like_bareword_expression_with_trailing_pipe() {
let parse = parse_source("string $name = AA_Bar*|mdl|_XXa0|;");
assert!(parse.errors.is_empty());
let Item::Stmt(stmt) = &parse.syntax.items[0] else {
panic!("expected statement");
};
let Stmt::VarDecl { decl, .. } = &**stmt else {
panic!("expected variable declaration");
};
let Some(Expr::BareWord { text, .. }) = decl.declarators[0].initializer.as_ref() else {
panic!("expected bareword initializer");
};
assert_eq!(parse.source_slice(*text), "AA_Bar*|mdl|_XXa0|");
}
#[test]
fn expression_mode_parses_direct_attribute_assignment_as_expression() {
let parse = parse_source_with_options("persp.translateX = 23.2;", ParseOptions::expression());
assert!(parse.errors.is_empty());
let Item::Stmt(stmt) = &parse.syntax.items[0] else {
panic!("expected statement");
};
let Stmt::Expr { expr, .. } = &**stmt else {
panic!("expected expression statement");
};
let Expr::Assign { lhs, rhs, .. } = expr else {
panic!("expected assignment");
};
assert!(matches!(**lhs, Expr::MemberAccess { .. }));
assert!(matches!(**rhs, Expr::Float { .. }));
}
#[test]
fn expression_mode_parses_particle_attribute_assignment() {
let parse = parse_source_with_options("position = <<2, 1, 0>>;", ParseOptions::expression());
assert!(parse.errors.is_empty());
let Item::Stmt(stmt) = &parse.syntax.items[0] else {
panic!("expected statement");
};
let Stmt::Expr { expr, .. } = &**stmt else {
panic!("expected expression statement");
};
let Expr::Assign { lhs, rhs, .. } = expr else {
panic!("expected assignment");
};
assert!(matches!(**lhs, Expr::Ident { .. }));
assert!(matches!(**rhs, Expr::VectorLiteral { .. }));
}
#[test]
fn expression_mode_parses_time_and_frame_identifiers() {
let parse = parse_source_with_options(
"persp.rotateY = time;\npersp.translateY = frame;",
ParseOptions::expression(),
);
assert!(parse.errors.is_empty());
assert_eq!(parse.syntax.items.len(), 2);
}
#[test]
fn expression_mode_keeps_obvious_command_statement_fallback() {
let parse = parse_source_with_options(r#"print "hello";"#, ParseOptions::expression());
assert!(parse.errors.is_empty());
let Item::Stmt(stmt) = &parse.syntax.items[0] else {
panic!("expected statement");
};
assert!(matches!(
&**stmt,
Stmt::Expr {
expr: Expr::Invoke(_),
..
}
));
}
#[test]
fn inline_expression_mode_allows_only_top_level_trailing_statement() {
let parse = parse_source_with_options("persp.translateY = frame", ParseOptions::expression());
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected ';' after statement");
let parse = parse_source_with_options(
"persp.translateY = frame",
ParseOptions::inline_expression(),
);
assert!(parse.errors.is_empty());
let parse = parse_source_with_options(
"if (ready) persp.translateY = frame",
ParseOptions::inline_expression(),
);
assert!(!parse.errors.is_empty());
assert_eq!(parse.errors[0].message, "expected ';' after statement");
}