use crate::language::Token;
use crate::parsers::cst::Parser;
use crate::parsers::SyntaxKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct BindingPower(u8);
impl BindingPower {
pub(super) const NONE: BindingPower = BindingPower(0);
pub(super) const IMP: BindingPower = BindingPower(10);
pub(super) const EQV: BindingPower = BindingPower(20);
pub(super) const XOR: BindingPower = BindingPower(30);
pub(super) const OR: BindingPower = BindingPower(40);
pub(super) const AND: BindingPower = BindingPower(50);
pub(super) const NOT: BindingPower = BindingPower(60);
pub(super) const COMPARISON: BindingPower = BindingPower(70);
pub(super) const CONCATENATION: BindingPower = BindingPower(80);
pub(super) const ADDITION: BindingPower = BindingPower(90);
pub(super) const MODULO: BindingPower = BindingPower(100);
pub(super) const INT_DIVISION: BindingPower = BindingPower(110);
pub(super) const MULTIPLICATION: BindingPower = BindingPower(120);
pub(super) const UNARY: BindingPower = BindingPower(130);
pub(super) const EXPONENTIATION: BindingPower = BindingPower(140);
}
impl Parser<'_> {
pub(crate) fn parse_expression(&mut self) {
self.parse_expression_with_binding_power(BindingPower::NONE);
}
pub(crate) fn parse_lvalue(&mut self) {
self.parse_expression_with_binding_power(BindingPower(75));
}
pub(crate) fn parse_expression_with_binding_power(&mut self, min_bp: BindingPower) {
self.consume_whitespace();
let lhs_checkpoint = self.builder.checkpoint();
let is_bare_identifier = self.parse_prefix_expression();
if is_bare_identifier {
self.builder
.start_node_at(lhs_checkpoint, SyntaxKind::IdentifierExpression.to_raw());
self.builder.finish_node();
}
loop {
let saved_pos = self.pos;
loop {
match self.current_token() {
Some(Token::Whitespace) => {
self.pos += 1;
}
Some(Token::Underscore) => {
let mut lookahead = 1;
let mut is_continuation = false;
while let Some((_, token)) = self.tokens.get(self.pos + lookahead) {
if *token == Token::Whitespace {
lookahead += 1;
} else if *token == Token::Newline {
is_continuation = true;
break;
} else {
break;
}
}
if is_continuation {
self.pos += lookahead + 1; } else {
break;
}
}
_ => break,
}
}
if self.is_at_end() || self.is_at_expression_delimiter() {
self.pos = saved_pos;
break;
}
let binding_power = self.get_infix_binding_power();
self.pos = saved_pos;
let Some((left_bp, right_bp)) = binding_power else {
break;
};
if left_bp < min_bp {
break;
}
self.consume_whitespace();
self.builder
.start_node_at(lhs_checkpoint, SyntaxKind::BinaryExpression.to_raw());
self.consume_token();
self.consume_whitespace();
self.parse_expression_with_binding_power(right_bp);
self.builder.finish_node();
}
}
fn parse_prefix_expression(&mut self) -> bool {
self.consume_whitespace();
let checkpoint = self.builder.checkpoint();
let mut is_identifier = false;
match self.current_token() {
Some(Token::SubtractionOperator) => {
self.parse_unary_expression(BindingPower::UNARY);
}
Some(Token::NotKeyword) => {
self.parse_unary_expression(BindingPower::NOT);
}
Some(Token::AddressOfKeyword) => {
self.parse_addressof_expression();
}
Some(Token::NewKeyword) => {
self.parse_new_expression();
}
Some(Token::LeftParenthesis) => {
self.parse_parenthesized_expression();
}
Some(
Token::IntegerLiteral
| Token::LongLiteral
| Token::SingleLiteral
| Token::DoubleLiteral
| Token::DecimalLiteral,
) => {
self.parse_numeric_literal();
}
Some(Token::StringLiteral) => {
self.parse_string_literal();
}
Some(Token::TrueKeyword | Token::FalseKeyword) => {
self.parse_boolean_literal();
}
Some(Token::NullKeyword | Token::EmptyKeyword) => {
self.parse_special_literal();
}
Some(Token::DateTimeLiteral) => {
self.parse_date_literal();
}
_ => {
self.parse_identifier_or_call_expression();
is_identifier = true;
}
}
let has_postfix = self.parse_postfix_operators_with_checkpoint(checkpoint);
is_identifier && !has_postfix
}
fn parse_identifier_or_call_expression(&mut self) {
if self.is_identifier() || self.at_keyword() {
if self.at_keyword_dollar() {
self.consume_keyword_dollar_as_identifier();
} else {
self.consume_token();
if matches!(
self.current_token(),
Some(
Token::DollarSign
| Token::Percent
| Token::Ampersand
| Token::ExclamationMark
| Token::Octothorpe
| Token::AtSign
)
) {
self.consume_token();
}
}
} else {
self.consume_token();
}
}
fn parse_unary_expression(&mut self, bp: BindingPower) {
self.builder
.start_node(SyntaxKind::UnaryExpression.to_raw());
self.consume_token();
self.consume_whitespace();
self.parse_expression_with_binding_power(bp);
self.builder.finish_node();
}
fn parse_addressof_expression(&mut self) {
self.builder
.start_node(SyntaxKind::AddressOfExpression.to_raw());
self.consume_token();
self.consume_whitespace();
if self.is_identifier() || self.at_keyword() {
self.consume_token();
}
self.builder.finish_node();
}
fn parse_new_expression(&mut self) {
self.builder.start_node(SyntaxKind::NewExpression.to_raw());
self.consume_token();
self.consume_whitespace();
if self.is_identifier() || self.at_keyword() {
self.consume_token();
}
self.builder.finish_node();
}
fn parse_parenthesized_expression(&mut self) {
self.builder
.start_node(SyntaxKind::ParenthesizedExpression.to_raw());
self.consume_token();
self.consume_whitespace();
self.parse_expression();
self.consume_whitespace();
if self.at_token(Token::RightParenthesis) {
self.consume_token();
}
self.builder.finish_node();
}
fn parse_numeric_literal(&mut self) {
self.builder
.start_node(SyntaxKind::NumericLiteralExpression.to_raw());
self.consume_token();
self.builder.finish_node();
}
fn parse_string_literal(&mut self) {
self.builder
.start_node(SyntaxKind::StringLiteralExpression.to_raw());
self.consume_token();
self.builder.finish_node();
}
fn parse_boolean_literal(&mut self) {
self.builder
.start_node(SyntaxKind::BooleanLiteralExpression.to_raw());
self.consume_token();
self.builder.finish_node();
}
fn parse_special_literal(&mut self) {
self.builder
.start_node(SyntaxKind::LiteralExpression.to_raw());
self.consume_token();
self.builder.finish_node();
}
fn parse_date_literal(&mut self) {
self.builder
.start_node(SyntaxKind::LiteralExpression.to_raw());
self.consume_token();
self.builder.finish_node();
}
fn parse_postfix_operators_with_checkpoint(&mut self, checkpoint: rowan::Checkpoint) -> bool {
let current_checkpoint = checkpoint;
let mut has_any_postfix = false;
loop {
let saved_pos = self.pos;
loop {
match self.current_token() {
Some(Token::Whitespace) => {
self.pos += 1;
}
Some(Token::Underscore) => {
let mut lookahead = 1;
let mut is_continuation = false;
while let Some((_, token)) = self.tokens.get(self.pos + lookahead) {
if *token == Token::Whitespace {
lookahead += 1;
} else if *token == Token::Newline {
is_continuation = true;
break;
} else {
break;
}
}
if is_continuation {
self.pos += lookahead + 1; } else {
break;
}
}
_ => break,
}
}
let found_postfix = matches!(
self.current_token(),
Some(Token::PeriodOperator | Token::LeftParenthesis | Token::ExclamationMark)
);
self.pos = saved_pos;
if !found_postfix {
break;
}
self.consume_whitespace();
match self.current_token() {
Some(Token::PeriodOperator) => {
self.builder.start_node_at(
current_checkpoint,
SyntaxKind::MemberAccessExpression.to_raw(),
);
has_any_postfix = true;
self.parse_member_access_content();
self.builder.finish_node();
}
Some(Token::LeftParenthesis) => {
self.builder
.start_node_at(current_checkpoint, SyntaxKind::CallExpression.to_raw());
has_any_postfix = true;
self.parse_call_or_index_content();
self.builder.finish_node();
}
Some(Token::ExclamationMark) => {
self.builder.start_node_at(
current_checkpoint,
SyntaxKind::MemberAccessExpression.to_raw(),
);
has_any_postfix = true;
self.parse_dictionary_access_content();
self.builder.finish_node();
}
_ => break,
}
}
has_any_postfix
}
fn parse_member_access_content(&mut self) {
self.consume_token();
self.consume_whitespace();
if self.is_identifier() || self.at_keyword() {
self.consume_token();
if matches!(
self.current_token(),
Some(
Token::DollarSign
| Token::Percent
| Token::Ampersand
| Token::ExclamationMark
| Token::Octothorpe
| Token::AtSign
)
) {
self.consume_token();
}
}
}
fn parse_call_or_index_content(&mut self) {
self.consume_token();
self.parse_argument_list_in_parens();
if self.at_token(Token::RightParenthesis) {
self.consume_token();
}
}
fn parse_dictionary_access_content(&mut self) {
self.consume_token();
self.consume_whitespace();
if self.is_identifier() || self.at_keyword() || self.at_token(Token::StringLiteral) {
self.consume_token();
}
}
pub(crate) fn parse_argument_list_in_parens(&mut self) {
self.builder.start_node(SyntaxKind::ArgumentList.to_raw());
self.consume_whitespace();
while !self.is_at_end() && !self.at_token(Token::RightParenthesis) {
self.builder.start_node(SyntaxKind::Argument.to_raw());
self.parse_expression();
self.builder.finish_node();
self.consume_whitespace();
if self.at_token(Token::Comma) {
self.consume_token();
self.consume_whitespace();
} else {
break;
}
}
self.builder.finish_node();
}
fn get_infix_binding_power(&self) -> Option<(BindingPower, BindingPower)> {
let token = self.current_token()?;
let (left_bp, right_bp) = match token {
Token::ExponentiationOperator => {
(BindingPower::EXPONENTIATION, BindingPower::EXPONENTIATION)
}
Token::MultiplicationOperator | Token::DivisionOperator => {
let bp = BindingPower::MULTIPLICATION;
(bp, BindingPower(bp.0 + 1))
}
Token::BackwardSlashOperator => {
let bp = BindingPower::INT_DIVISION;
(bp, BindingPower(bp.0 + 1))
}
Token::ModKeyword => {
let bp = BindingPower::MODULO;
(bp, BindingPower(bp.0 + 1))
}
Token::AdditionOperator | Token::SubtractionOperator => {
let bp = BindingPower::ADDITION;
(bp, BindingPower(bp.0 + 1))
}
Token::Ampersand => {
let bp = BindingPower::CONCATENATION;
(bp, BindingPower(bp.0 + 1))
}
Token::EqualityOperator
| Token::InequalityOperator
| Token::LessThanOrEqualOperator
| Token::GreaterThanOrEqualOperator
| Token::LessThanOperator
| Token::GreaterThanOperator
| Token::LikeKeyword
| Token::IsKeyword => {
let bp = BindingPower::COMPARISON;
(bp, BindingPower(bp.0 + 1))
}
Token::AndKeyword => {
let bp = BindingPower::AND;
(bp, BindingPower(bp.0 + 1))
}
Token::OrKeyword => {
let bp = BindingPower::OR;
(bp, BindingPower(bp.0 + 1))
}
Token::XorKeyword => {
let bp = BindingPower::XOR;
(bp, BindingPower(bp.0 + 1))
}
Token::EqvKeyword => {
let bp = BindingPower::EQV;
(bp, BindingPower(bp.0 + 1))
}
Token::ImpKeyword => {
let bp = BindingPower::IMP;
(bp, BindingPower(bp.0 + 1))
}
_ => return None,
};
Some((left_bp, right_bp))
}
fn is_at_expression_delimiter(&self) -> bool {
matches!(
self.current_token(),
Some(
Token::Newline
| Token::ThenKeyword
| Token::ToKeyword
| Token::StepKeyword
| Token::ColonOperator
| Token::EndOfLineComment
| Token::RemComment
| Token::Comma
| Token::RightParenthesis
)
)
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn numeric_literal() {
let source = "x = 42\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn numeric_literal_with_type_suffix() {
let source = "x = 42%\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn string_literal() {
let source = "x = \"Hello, World!\"\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn boolean_literal_true() {
let source = "x = True\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn boolean_literal_false() {
let source = "x = False\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn identifier_expression() {
let source = "x = myVariable\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn simple_addition() {
let source = "x = 2 + 3\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn simple_subtraction() {
let source = "x = 10 - 5\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn simple_multiplication() {
let source = "x = 4 * 5\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn simple_division() {
let source = "x = 20 / 4\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn operator_precedence_multiplication_before_addition() {
let source = "x = 2 + 3 * 4\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn operator_precedence_left_associativity() {
let source = "x = 10 - 5 - 2\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn unary_negation() {
let source = "x = -5\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn logical_not() {
let source = "x = Not True\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn logical_and() {
let source = "x = True And False\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn logical_or() {
let source = "x = True Or False\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn comparison_equal() {
let source = "x = a = b\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn comparison_less_than() {
let source = "x = a < b\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn comparison_greater_than() {
let source = "x = a > b\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn parenthesized_expression() {
let source = "x = (5 + 3)\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn parenthesized_changes_precedence() {
let source = "x = (2 + 3) * 4\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn member_access() {
let source = "x = obj.property\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn chained_member_access() {
let source = "x = obj.prop1.prop2\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn function_call_no_args() {
let source = "x = MyFunction()\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn function_call_one_arg() {
let source = "x = MyFunction(42)\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn function_call_multiple_args() {
let source = "x = MyFunction(1, 2, 3)\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn method_call() {
let source = "x = obj.Method(arg)\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn new_expression() {
let source = "Set x = New MyClass\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn addressof_expression() {
let source = "x = AddressOf MyProc\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn string_concatenation() {
let source = "x = \"Hello\" & \" \" & \"World\"\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn modulo_operator() {
let source = "x = 10 Mod 3\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn integer_division() {
let source = "x = 10 \\ 3\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn exponentiation() {
let source = "x = 2 ^ 8\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn complex_arithmetic() {
let source = "x = (a + b) * c - d / e\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn complex_logical() {
let source = "x = Not a And b Or c\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn nothing_literal() {
let source = "Set x = Nothing\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn null_literal() {
let source = "x = Null\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn empty_literal() {
let source = "x = Empty\n";
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
#[test]
fn dollar_sign_functions_merged() {
let source = r#"
x = Chr$(65)
y = UCase$("hello")
z = Left$("test", 2)
"#;
let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
let cst = cst_opt.expect("CST should be parsed");
let tree = cst.to_serializable();
let mut settings = insta::Settings::clone_current();
settings.set_snapshot_path("../../../snapshots/syntax/expressions");
settings.set_prepend_module_to_snapshot(false);
let _guard = settings.bind_to_scope();
insta::assert_yaml_snapshot!(tree);
}
}