Skip to content

Commit

Permalink
Single line variable declarations (#226)
Browse files Browse the repository at this point in the history
* implement single line variable declarations

* fix location range on inline structs

* exclude tests from coverage
  • Loading branch information
ulmer-a authored Aug 10, 2021
1 parent 2200c8c commit e159877
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 35 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions src/index/tests/index_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
Expand Down Expand Up @@ -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()),
Expand Down
68 changes: 45 additions & 23 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,12 +350,7 @@ fn parse_data_type_definition(
) -> Option<DataTypeWithInitializer> {
if lexer.allow(&KeywordStruct) {
// Parse 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);
Some((
DataTypeDeclaration::DataTypeDefinition {
data_type: DataType::StructType { name, variables },
Expand Down Expand Up @@ -585,36 +580,63 @@ 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);
}
}
variables
parse_variable_list(lexer)
});
VariableBlock {
variables,
variable_block_type,
}
}

fn parse_variable(lexer: &mut ParseSession) -> Option<Variable> {
let variable_location = lexer.location();
let name = lexer.slice_and_advance();
fn parse_variable_list(lexer: &mut ParseSession) -> Vec<Variable> {
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<Variable> {
// read in a comma separated list of variable names
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.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),
));
}
}

//parse or recover until the colon
// colon has to come before the data type
if !lexer.allow(&KeywordColon) {
lexer.accept_diagnostic(Diagnostic::missing_token(
format!("{:?}", KeywordColon),
lexer.location(),
));
}

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
}
35 changes: 25 additions & 10 deletions src/parser/tests/parse_errors/parse_error_statements_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ fn invalid_variable_data_type_error_recovery() {
VAR
a DINT : ;
c : INT;
h , , : INT;
f , INT : ;
END_VAR
END_PROGRAM
");
Expand All @@ -323,12 +325,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 {
Expand All @@ -343,12 +339,31 @@ 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)
),
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)
),
]
);
}
Expand Down
51 changes: 51 additions & 0 deletions src/parser/tests/variable_parser_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,57 @@ 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).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");
Expand Down

0 comments on commit e159877

Please sign in to comment.