From c26bdd4f08830259fd2eac53bc3f50d7a7534b27 Mon Sep 17 00:00:00 2001 From: Volkan Sagcan Date: Tue, 8 Nov 2022 19:43:07 +0100 Subject: [PATCH 1/3] fix: Support literal numbers with explicit plus sign (#633) Fixes issue #633 where previously the compiler would yield an error if it encountered a number with an explicit plus sign, i.e. the following was previously not supported but is now ``` foo : DINT := +2147483647; ``` --- src/parser/expressions_parser.rs | 38 +++++++++++--------- src/parser/tests/expressions_parser_tests.rs | 31 ++++++++++++++++ 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/parser/expressions_parser.rs b/src/parser/expressions_parser.rs index d1a2b08683..55b6cfd66b 100644 --- a/src/parser/expressions_parser.rs +++ b/src/parser/expressions_parser.rs @@ -255,22 +255,27 @@ fn parse_leaf_expression(lexer: &mut ParseSession) -> AstStatement { None }; - let literal_parse_result = if lexer.allow(&OperatorMinus) { - //so we've seen a Minus '-', this has to be a number - match lexer.token { - LiteralInteger => parse_literal_number(lexer, true), - LiteralIntegerBin => parse_literal_number_with_modifier(lexer, 2, true), - LiteralIntegerOct => parse_literal_number_with_modifier(lexer, 8, true), - LiteralIntegerHex => parse_literal_number_with_modifier(lexer, 16, true), - _ => Err(Diagnostic::unexpected_token_found( - "Numeric Literal", - lexer.slice(), - lexer.location(), - )), + let literal_parse_result = match lexer.token { + // Check if we're dealing with a number that has an explicit '+' or '-' sign... + OperatorPlus | OperatorMinus => { + let is_negative = lexer.token == OperatorMinus; + lexer.advance(); + + match lexer.token { + LiteralInteger => parse_literal_number(lexer, is_negative), + LiteralIntegerBin => parse_literal_number_with_modifier(lexer, 2, is_negative), + LiteralIntegerOct => parse_literal_number_with_modifier(lexer, 8, is_negative), + LiteralIntegerHex => parse_literal_number_with_modifier(lexer, 16, is_negative), + _ => Err(Diagnostic::unexpected_token_found( + "Numeric Literal", + lexer.slice(), + lexer.location(), + )), + } } - } else { - // no minus ... so this may be anything - match lexer.token { + + // ...and if not then this token may be anything + _ => match lexer.token { Identifier => parse_qualified_reference(lexer), LiteralInteger => parse_literal_number(lexer, false), LiteralIntegerBin => parse_literal_number_with_modifier(lexer, 2, false), @@ -308,8 +313,9 @@ fn parse_leaf_expression(lexer: &mut ParseSession) -> AstStatement { )) } } - } + }, }; + let literal_parse_result = literal_parse_result.and_then(|statement| { if let Some((cast, location)) = literal_cast { //check if there is something between the literal-type and the literal itself diff --git a/src/parser/tests/expressions_parser_tests.rs b/src/parser/tests/expressions_parser_tests.rs index 9dac1a0c02..0c0d040ebd 100644 --- a/src/parser/tests/expressions_parser_tests.rs +++ b/src/parser/tests/expressions_parser_tests.rs @@ -1513,6 +1513,37 @@ fn assignment_to_null() { assert_eq!(ast_string, expected_ast); } +#[test] +fn assignment_to_number_with_implicit_and_explicit_plus_sign() { + let src = " + PROGRAM exp + x : DINT := 1; + y : DINT := +1; + END_PROGRAM + "; + + let result = parse(src).0; + let statements = &result.implementations[0].statements; + + println!("{statements:#?}"); + let ast_string_implicit = format!("{:#?}", statements[1]); + let ast_string_explicit = format!("{:#?}", statements[3]); + + let expected_ast = r#"Assignment { + left: Reference { + name: "DINT", + }, + right: LiteralInteger { + value: 1, + }, +}"#; + + // Both the implicit and explicit assignment should yield the same output + // which in turn should be `expected_ast` + assert_eq!(ast_string_implicit, ast_string_explicit); + assert_eq!(ast_string_implicit, expected_ast); +} + #[test] fn pointer_address_test() { let src = " From 64d752e7095d5f16ddd985e84c7f705ede817ef6 Mon Sep 17 00:00:00 2001 From: Volkan Sagcan Date: Tue, 8 Nov 2022 20:22:32 +0100 Subject: [PATCH 2/3] Remove `println!` --- src/parser/tests/expressions_parser_tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parser/tests/expressions_parser_tests.rs b/src/parser/tests/expressions_parser_tests.rs index 0c0d040ebd..405fc5f365 100644 --- a/src/parser/tests/expressions_parser_tests.rs +++ b/src/parser/tests/expressions_parser_tests.rs @@ -1525,7 +1525,6 @@ fn assignment_to_number_with_implicit_and_explicit_plus_sign() { let result = parse(src).0; let statements = &result.implementations[0].statements; - println!("{statements:#?}"); let ast_string_implicit = format!("{:#?}", statements[1]); let ast_string_explicit = format!("{:#?}", statements[3]); From df46a75f7de11e8290854da0ce77aaca3dc80fb3 Mon Sep 17 00:00:00 2001 From: Volkan Sagcan Date: Wed, 9 Nov 2022 21:04:24 +0100 Subject: [PATCH 3/3] Implement PR feedback --- src/parser/expressions_parser.rs | 36 ++++++++++----- src/parser/tests/expressions_parser_tests.rs | 47 ++++++++++++++++---- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/parser/expressions_parser.rs b/src/parser/expressions_parser.rs index 55b6cfd66b..3f3a5efdd5 100644 --- a/src/parser/expressions_parser.rs +++ b/src/parser/expressions_parser.rs @@ -171,6 +171,7 @@ fn parse_exponent_expression(lexer: &mut ParseSession) -> AstStatement { fn parse_unary_expression(lexer: &mut ParseSession) -> AstStatement { let operator = match lexer.token { OperatorNot => Some(Operator::Not), + OperatorPlus => Some(Operator::Plus), OperatorMinus => Some(Operator::Minus), OperatorAmp => Some(Operator::Address), _ => None, @@ -185,23 +186,36 @@ fn parse_unary_expression(lexer: &mut ParseSession) -> AstStatement { .source_range_factory .create_range(start..expression_location.get_end()); - if let (AstStatement::LiteralInteger { value, .. }, Operator::Minus) = - (&expression, &operator) - { - //if this turns out to be a negative number, we want to have a negative literal integer - //instead of a Unary-Not-Expression - AstStatement::LiteralInteger { - value: -value, + match (&operator, &expression) { + (Operator::Minus, AstStatement::LiteralInteger { value, .. }) => { + AstStatement::LiteralInteger { + value: -value, + location, + id: lexer.next_id(), + } + } + + (Operator::Plus, AstStatement::LiteralInteger { value, .. }) => { + AstStatement::LiteralInteger { + value: *value, + location, + id: lexer.next_id(), + } + } + + // Return the reference itself instead of wrapping it inside a `AstStatement::UnaryExpression` + (Operator::Plus, AstStatement::Reference { name, .. }) => AstStatement::Reference { + name: name.to_owned(), location, id: lexer.next_id(), - } - } else { - AstStatement::UnaryExpression { + }, + + _ => AstStatement::UnaryExpression { operator, value: Box::new(expression), location, id: lexer.next_id(), - } + }, } } else { parse_parenthesized_expression(lexer) diff --git a/src/parser/tests/expressions_parser_tests.rs b/src/parser/tests/expressions_parser_tests.rs index 405fc5f365..f3b2f239df 100644 --- a/src/parser/tests/expressions_parser_tests.rs +++ b/src/parser/tests/expressions_parser_tests.rs @@ -1516,33 +1516,62 @@ fn assignment_to_null() { #[test] fn assignment_to_number_with_implicit_and_explicit_plus_sign() { let src = " - PROGRAM exp - x : DINT := 1; - y : DINT := +1; + PROGRAM exp + VAR + x : INT; + END_VAR + x := 1; + x := +1; END_PROGRAM "; let result = parse(src).0; let statements = &result.implementations[0].statements; - let ast_string_implicit = format!("{:#?}", statements[1]); - let ast_string_explicit = format!("{:#?}", statements[3]); - + let ast_string_implicit = format!("{:#?}", statements[0]); + let ast_string_explicit = format!("{:#?}", statements[1]); let expected_ast = r#"Assignment { left: Reference { - name: "DINT", + name: "x", }, right: LiteralInteger { value: 1, }, }"#; - // Both the implicit and explicit assignment should yield the same output - // which in turn should be `expected_ast` + // Both the implicit and explicit assignment should yield the same output, namely `expected_ast` assert_eq!(ast_string_implicit, ast_string_explicit); assert_eq!(ast_string_implicit, expected_ast); } +#[test] +fn assignment_to_number_reference_with_explicit_plus_sign() { + let src = " + PROGRAM exp + VAR + x : INT; + END_VAR + x := 1; + x := +x; + END_PROGRAM + "; + + let result = parse(src).0; + let statements = &result.implementations[0].statements; + + let ast_string_explicit = format!("{:#?}", statements[1]); + let expected_ast = r#"Assignment { + left: Reference { + name: "x", + }, + right: Reference { + name: "x", + }, +}"#; + + assert_eq!(ast_string_explicit, expected_ast); +} + #[test] fn pointer_address_test() { let src = "