From aafcb2876d39d9b9d22c976c9360428ebd74083d Mon Sep 17 00:00:00 2001 From: Alexander Ulmer Date: Fri, 6 Aug 2021 10:23:25 +0200 Subject: [PATCH 1/6] add test --- src/parser/tests/variable_parser_tests.rs | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/parser/tests/variable_parser_tests.rs b/src/parser/tests/variable_parser_tests.rs index 3b86360d66..107c58d0a7 100644 --- a/src/parser/tests/variable_parser_tests.rs +++ b/src/parser/tests/variable_parser_tests.rs @@ -41,6 +41,58 @@ fn global_vars_can_be_parsed() { assert_eq!(ast_string, expected_ast) } +#[test] +fn global_single_line_vars_can_be_parsed() { + let lexer = lex("VAR_GLOBAL x, y,z : INT; f : BOOL; b, c : SINT; END_VAR"); + let result = parse(lexer).unwrap().0; + + let vars = &result.global_vars[0]; //globar_vars + let ast_string = format!("{:#?}", vars); + let expected_ast = r#"VariableBlock { + variables: [ + Variable { + name: "x", + data_type: DataTypeReference { + referenced_type: "INT", + }, + }, + Variable { + name: "y", + data_type: DataTypeReference { + referenced_type: "INT", + }, + }, + Variable { + name: "z", + data_type: DataTypeReference { + referenced_type: "INT", + }, + }, + Variable { + name: "f", + data_type: DataTypeReference { + referenced_type: "BOOL", + }, + }, + Variable { + name: "b", + data_type: DataTypeReference { + referenced_type: "SINT", + }, + }, + Variable { + name: "c", + data_type: DataTypeReference { + referenced_type: "SINT", + }, + }, + ], + variable_block_type: Global, +}"#; + assert_eq!(ast_string, expected_ast) +} + + #[test] fn two_global_vars_can_be_parsed() { let lexer = lex("VAR_GLOBAL a: INT; END_VAR VAR_GLOBAL x : INT; y : BOOL; END_VAR"); From bc2efd5eb8b2d409a5bf854b0b8bf6708839151b Mon Sep 17 00:00:00 2001 From: Alexander Ulmer Date: Fri, 6 Aug 2021 14:15:20 +0200 Subject: [PATCH 2/6] implement single line variable declarations --- src/parser.rs | 58 ++++++++++++++--------- src/parser/tests/variable_parser_tests.rs | 1 - 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 0fe406076b..45232a18db 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -359,12 +359,7 @@ fn parse_data_type_definition( ) -> Result { let result = if lexer.allow(&KeywordStruct) { //STRUCT - let mut variables = Vec::new(); - while lexer.token == Identifier { - if let Some(variable) = parse_variable(lexer) { - variables.push(variable); - } - } + let variables = parse_variable_list(lexer); Ok(( DataTypeDeclaration::DataTypeDefinition { data_type: DataType::StructType { name, variables }, @@ -617,13 +612,7 @@ fn parse_variable_block( //Consume the type keyword lexer.advance(); let variables = parse_any_in_region(lexer, vec![KeywordEndVar], |lexer| { - let mut variables = vec![]; - while lexer.token == Identifier { - if let Some(variable) = parse_variable(lexer) { - variables.push(variable); - } - } - Ok(variables) + Ok(parse_variable_list(lexer)) }) .unwrap_or_default(); VariableBlock { @@ -632,11 +621,27 @@ fn parse_variable_block( } } -fn parse_variable(lexer: &mut ParseSession) -> Option { - let variable_location = lexer.location(); - let name = lexer.slice_and_advance(); +fn parse_variable_list(lexer: &mut ParseSession) -> Vec { + let mut variables = vec![]; + while lexer.token == Identifier { + let mut line_vars = parse_variable_line(lexer); + variables.append(&mut line_vars); + } + variables +} + +fn parse_variable_line(lexer: &mut ParseSession) -> Vec { + // read in a comma separated list of variable names + let mut var_names: Vec<(String, SourceRange)> = vec![]; + while lexer.token == Identifier { + var_names.push((lexer.slice_and_advance(), lexer.location())); - //parse or recover until the colon + if !lexer.allow(&KeywordComma) { + break; + } + } + + // colon has to come before the data type if !lexer.allow(&KeywordColon) { lexer.accept_diagnostic(Diagnostic::missing_token( format!("{:?}", KeywordColon), @@ -644,10 +649,17 @@ fn parse_variable(lexer: &mut ParseSession) -> Option { )); } - parse_full_data_type_definition(lexer, None).map(|(data_type, initializer)| Variable { - name, - data_type, - location: variable_location, - initializer, - }) + // create variables with the same data type for each of the names + let mut variables = vec![]; + if let Some((data_type, initializer)) = parse_full_data_type_definition(lexer, None) { + for (name, location) in var_names { + variables.push(Variable { + name, + data_type: data_type.clone(), + location, + initializer: initializer.clone(), + }); + } + } + variables } diff --git a/src/parser/tests/variable_parser_tests.rs b/src/parser/tests/variable_parser_tests.rs index 107c58d0a7..f9b557ed4d 100644 --- a/src/parser/tests/variable_parser_tests.rs +++ b/src/parser/tests/variable_parser_tests.rs @@ -92,7 +92,6 @@ fn global_single_line_vars_can_be_parsed() { assert_eq!(ast_string, expected_ast) } - #[test] fn two_global_vars_can_be_parsed() { let lexer = lex("VAR_GLOBAL a: INT; END_VAR VAR_GLOBAL x : INT; y : BOOL; END_VAR"); From 3a68e834be90d3e11d15313da8eaac9c68d6baff Mon Sep 17 00:00:00 2001 From: Alexander Ulmer Date: Fri, 6 Aug 2021 16:33:25 +0200 Subject: [PATCH 3/6] fix location range on inline structs --- src/index/tests/index_tests.rs | 13 +++++++++++++ src/parser.rs | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/index/tests/index_tests.rs b/src/index/tests/index_tests.rs index 29a69e599d..8f646d36a9 100644 --- a/src/index/tests/index_tests.rs +++ b/src/index/tests/index_tests.rs @@ -614,6 +614,13 @@ fn pre_processing_generates_inline_structs_global() { //STRUCT //THEN an implicit datatype should have been generated for the struct let new_struct_type = &ast.types[0].data_type; + + if let DataType::StructType { variables, .. } = new_struct_type { + assert_eq!(variables[0].location, SourceRange::new(54..55)); + } else { + panic!("expected struct") + } + assert_eq!( &DataType::StructType { name: Some("__global_inline_struct".to_string()), @@ -693,6 +700,12 @@ fn pre_processing_generates_inline_structs() { //STRUCT //THEN an implicit datatype should have been generated for the struct let new_struct_type = &ast.types[0].data_type; + if let DataType::StructType { variables, .. } = new_struct_type { + assert_eq!(variables[0].location, SourceRange::new(67..68)); + } else { + panic!("expected struct") + } + assert_eq!( &DataType::StructType { name: Some("__foo_inline_struct".to_string()), diff --git a/src/parser.rs b/src/parser.rs index ba6fba16c2..8000907005 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -601,7 +601,8 @@ fn parse_variable_line(lexer: &mut ParseSession) -> Vec { // read in a comma separated list of variable names let mut var_names: Vec<(String, SourceRange)> = vec![]; while lexer.token == Identifier { - var_names.push((lexer.slice_and_advance(), lexer.location())); + let location = lexer.location(); + var_names.push((lexer.slice_and_advance(), location)); if !lexer.allow(&KeywordComma) { break; From 6658640fb6ace843ac336da3a247a236536fe086 Mon Sep 17 00:00:00 2001 From: Alexander Ulmer Date: Fri, 6 Aug 2021 20:09:45 +0200 Subject: [PATCH 4/6] exclude tests from coverage --- .github/workflows/rust.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 34f01a631f..9696aaa1fb 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -173,8 +173,14 @@ jobs: use-tool-cache: true - name: Generate coverage report - run: - grcov . --binary-path ./target/debug/ -s . -t lcov --branch --ignore "src/lexer/tokens.rs" --ignore "src/main.rs" --ignore "src/parser/tests/*" --ignore-not-existing --ignore "/*" -o lcov.info + run: | + grcov . --binary-path ./target/debug/ -s . -t lcov --branch \ + --ignore "/*" \ + --ignore "src/main.rs" \ + --ignore "src/*/tests.rs" \ + --ignore "src/*/tests/*" \ + --ignore "src/lexer/tokens.rs" \ + --ignore-not-existing -o lcov.info - name: Upload to codecov.io From 313ccf95ffb551c681e43f88ca8d45eeafbc71a4 Mon Sep 17 00:00:00 2001 From: Alexander Ulmer Date: Mon, 9 Aug 2021 15:10:30 +0200 Subject: [PATCH 5/6] improve error recovery --- src/parser.rs | 11 ++++++++++- .../parse_errors/parse_error_statements_tests.rs | 15 ++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 8000907005..74718b3fdc 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -602,11 +602,20 @@ fn parse_variable_line(lexer: &mut ParseSession) -> Vec { let mut var_names: Vec<(String, SourceRange)> = vec![]; while lexer.token == Identifier { let location = lexer.location(); + let identifier_end = location.get_end(); var_names.push((lexer.slice_and_advance(), location)); - if !lexer.allow(&KeywordComma) { + if lexer.token == KeywordColon { break; } + + if !lexer.allow(&KeywordComma) { + let next_token_start = lexer.location().get_start(); + lexer.accept_diagnostic(Diagnostic::missing_token( + format!("{:?} or {:?}", KeywordColon, KeywordComma), + SourceRange::new(identifier_end..next_token_start), + )); + } } // colon has to come before the data type diff --git a/src/parser/tests/parse_errors/parse_error_statements_tests.rs b/src/parser/tests/parse_errors/parse_error_statements_tests.rs index 4d97b7e273..6697802c1a 100644 --- a/src/parser/tests/parse_errors/parse_error_statements_tests.rs +++ b/src/parser/tests/parse_errors/parse_error_statements_tests.rs @@ -323,12 +323,6 @@ fn invalid_variable_data_type_error_recovery() { format!("{:#?}", pou.variable_blocks[0]), r#"VariableBlock { variables: [ - Variable { - name: "a", - data_type: DataTypeReference { - referenced_type: "DINT", - }, - }, Variable { name: "c", data_type: DataTypeReference { @@ -343,11 +337,14 @@ fn invalid_variable_data_type_error_recovery() { assert_eq!( diagnostics, vec![ - Diagnostic::missing_token("KeywordColon".into(), SourceRange::new(54..58)), + Diagnostic::missing_token( + "KeywordColon or KeywordComma".into(), + SourceRange::new(53..54) + ), Diagnostic::unexpected_token_found( + "DataTypeDefinition".into(), "KeywordSemicolon".into(), - "':'".into(), - SourceRange::new(59..60) + SourceRange::new(61..62) ) ] ); From 069bcebfdeb770d773e9de7f9bdadc62f86b8339 Mon Sep 17 00:00:00 2001 From: Alexander Ulmer Date: Mon, 9 Aug 2021 15:30:11 +0200 Subject: [PATCH 6/6] add tests --- .../parse_error_statements_tests.rs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/parser/tests/parse_errors/parse_error_statements_tests.rs b/src/parser/tests/parse_errors/parse_error_statements_tests.rs index 6697802c1a..a033f0ee22 100644 --- a/src/parser/tests/parse_errors/parse_error_statements_tests.rs +++ b/src/parser/tests/parse_errors/parse_error_statements_tests.rs @@ -313,6 +313,8 @@ fn invalid_variable_data_type_error_recovery() { VAR a DINT : ; c : INT; + h , , : INT; + f , INT : ; END_VAR END_PROGRAM "); @@ -345,7 +347,23 @@ fn invalid_variable_data_type_error_recovery() { "DataTypeDefinition".into(), "KeywordSemicolon".into(), SourceRange::new(61..62) - ) + ), + Diagnostic::missing_token("KeywordColon".into(), SourceRange::new(108..109)), + Diagnostic::unexpected_token_found( + "DataTypeDefinition".into(), + "KeywordComma".into(), + SourceRange::new(108..109) + ), + Diagnostic::unexpected_token_found( + "KeywordSemicolon".into(), + "', : INT'".into(), + SourceRange::new(108..115) + ), + Diagnostic::unexpected_token_found( + "DataTypeDefinition".into(), + "KeywordSemicolon".into(), + SourceRange::new(143..144) + ), ] ); }