Skip to content

Commit

Permalink
feat: methods
Browse files Browse the repository at this point in the history
  • Loading branch information
mhasel committed Nov 13, 2024
1 parent 36769b5 commit 4747a29
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,15 @@ fn parse_pou(
// declarations before their implementation.
// all other Pous need to be checked in the validator if they can have methods.
while matches!(lexer.token, KeywordMethod | PropertyConstant) {
if !matches!(pou_type, PouType::FunctionBlock | PouType::Class | PouType::Program) {
let location = lexer.source_range_factory.create_range(lexer.last_range.clone());

lexer.accept_diagnostic(
Diagnostic::new(format!("Methods cannot be declared in a POU of type '{pou_type}'."))
.with_location(location),
);
break;
}
let const_method = lexer.try_consume(&PropertyConstant);
if let Some((pou, implementation)) = parse_method(lexer, &name, linkage, const_method) {
impl_pous.push(pou);
Expand Down
124 changes: 123 additions & 1 deletion src/parser/tests/class_parser_tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use insta::assert_snapshot;
use plc_ast::ast::{AccessModifier, ArgumentProperty, PolymorphismMode, PouType, VariableBlockType};

use crate::test_utils::tests::parse;
use crate::test_utils::tests::{parse, parse_buffered};

#[test]
fn simple_class_with_defaults_can_be_parsed() {
Expand Down Expand Up @@ -340,3 +341,124 @@ fn fb_method_with_return_type_can_be_parsed() {
assert_ne!(method_pou.return_type, None);
assert_eq!(method.overriding, true);
}

#[test]
fn program_methods_can_be_parsed() {
let src = r#"
PROGRAM prog
METHOD INTERNAL FINAL OVERRIDE testMethod2 END_METHOD
END_PROG
"#;
let unit = parse(src).0;

let class = &unit.units[0];
assert_eq!(class.pou_type, PouType::Program);

// classes have implementation because they are treated as other POUs
assert_eq!(unit.implementations.len(), 2);

let method_pou = &unit.units[1];
assert_eq!(method_pou.pou_type, PouType::Method { owner_class: "prog".into() });
let method = &unit.implementations[0];

assert_eq!(method_pou.name, "prog.testMethod2");
assert_eq!(method.access, Some(AccessModifier::Internal));
assert_eq!(method_pou.poly_mode, Some(PolymorphismMode::Final));
assert_eq!(method_pou.return_type, None);
assert_eq!(method.overriding, true);
}

#[test]
fn program_two_methods_can_be_parsed() {
let src = r#"
PROGRAM prog
METHOD INTERNAL testMethod2 END_METHOD
METHOD otherMethod VAR_TEMP END_VAR END_METHOD
END_PROGRAM
"#;
let unit = parse(src).0;

let class = &unit.units[0];
assert_eq!(class.pou_type, PouType::Program);

// classes have implementation because they are treated as other POUs
assert_eq!(unit.implementations.len(), 3);

let method1 = &unit.implementations[0];
assert_eq!(method1.name, "prog.testMethod2");
assert_eq!(method1.access, Some(AccessModifier::Internal));

let method2 = &unit.implementations[1];
assert_eq!(method2.name, "prog.otherMethod");
assert_eq!(method2.access, Some(AccessModifier::Protected));
}

#[test]
fn program_method_with_return_type_can_be_parsed() {
let src = r#"
PROGRAM prog
METHOD PRIVATE ABSTRACT OVERRIDE testMethod3 : SINT END_METHOD
END_PROGRAM
"#;
let unit = parse(src).0;

let class = &unit.units[0];
assert_eq!(class.pou_type, PouType::Program);

let method_pou = &unit.units[1];
assert_eq!(method_pou.pou_type, PouType::Method { owner_class: "prog".into() });
let method = &unit.implementations[0];

// classes have implementation because they are treated as other POUs
assert_eq!(unit.implementations.len(), 2);

assert_eq!(method_pou.name, "prog.testMethod3");
assert_eq!(method.access, Some(AccessModifier::Private)); // TODO: default public?
assert_eq!(method_pou.poly_mode, Some(PolymorphismMode::Abstract));
assert_ne!(method_pou.return_type, None);
assert_eq!(method.overriding, true);
}

#[test]
fn declaring_methods_in_functions_is_an_error() {
let src = r#"
FUNCTION bar
METHOD anyMethod
;
END_METHOD
;
END_FUNCTION
"#;
let (_, diagnostics) = parse_buffered(src);
assert_snapshot!(diagnostics, @r###"
error[E001]: Methods cannot be declared in a POU of type 'Function'.
┌─ <internal>:2:14
2 │ FUNCTION bar
│ ^^^ Methods cannot be declared in a POU of type 'Function'.
error[E007]: Unexpected token: expected Literal but found METHOD
┌─ <internal>:3:9
3 │ METHOD anyMethod
│ ^^^^^^ Unexpected token: expected Literal but found METHOD
error[E007]: Unexpected token: expected KeywordSemicolon but found 'METHOD anyMethod'
┌─ <internal>:3:9
3 │ METHOD anyMethod
│ ^^^^^^^^^^^^^^^^ Unexpected token: expected KeywordSemicolon but found 'METHOD anyMethod'
error[E007]: Unexpected token: expected Literal but found END_METHOD
┌─ <internal>:5:9
5 │ END_METHOD
│ ^^^^^^^^^^ Unexpected token: expected Literal but found END_METHOD
error[E007]: Unexpected token: expected KeywordSemicolon but found 'END_METHOD'
┌─ <internal>:5:9
5 │ END_METHOD
│ ^^^^^^^^^^ Unexpected token: expected KeywordSemicolon but found 'END_METHOD'
"###);
}

0 comments on commit 4747a29

Please sign in to comment.