Skip to content

Commit 3f70a78

Browse files
GitExampleghaith
GitExample
authored andcommitted
Add parsing support for inline variable declaration
Variables can be defined in the body using the syntax ```st {def} VAR y : DINT := 0; (*Pragma to avoid confusion with var blocks *) (*Pragma is optional once in body*) VAR x := 0; (*Implicit type declaration to DINT*) FOR VAR x := 0 TO 10 BEGIN (*Implicit variable declaration as loop counter*) END_FOR ``` Ref: #973
1 parent cd92cca commit 3f70a78

12 files changed

+634
-65
lines changed

compiler/plc_ast/src/ast.rs

+36
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,8 @@ pub enum AstStatement {
619619
ReturnStatement(ReturnStatement),
620620
JumpStatement(JumpStatement),
621621
LabelStatement(LabelStatement),
622+
InlineVariable(InlineVariable),
623+
DataTypeDeclaration(Box<DataTypeDeclaration>),
622624
}
623625

624626
impl Debug for AstNode {
@@ -738,6 +740,12 @@ impl Debug for AstNode {
738740
AstStatement::LabelStatement(LabelStatement { name, .. }) => {
739741
f.debug_struct("LabelStatement").field("name", name).finish()
740742
}
743+
AstStatement::InlineVariable(InlineVariable { name, datatype }) => {
744+
f.debug_struct("InlineVariable").field("name", name).field("datatype", datatype).finish()
745+
}
746+
AstStatement::DataTypeDeclaration(decl) => {
747+
f.debug_tuple("DataTypeDeclaration").field(decl).finish()
748+
}
741749
}
742750
}
743751
}
@@ -1512,6 +1520,27 @@ impl AstFactory {
15121520
pub fn create_label_statement(name: String, location: SourceLocation, id: AstId) -> AstNode {
15131521
AstNode { stmt: AstStatement::LabelStatement(LabelStatement { name }), location, id }
15141522
}
1523+
1524+
/// Creates a new inline declaration by boxing the name and datatype
1525+
pub fn create_inline_declaration(
1526+
name: AstNode,
1527+
datatype: Option<AstNode>,
1528+
id: AstId,
1529+
location: SourceLocation,
1530+
) -> AstNode {
1531+
let name = Box::new(name);
1532+
let datatype = datatype.map(Box::new);
1533+
AstNode { stmt: AstStatement::InlineVariable(InlineVariable { name, datatype }), id, location }
1534+
}
1535+
1536+
pub fn create_type_declaration(
1537+
datatype: DataTypeDeclaration,
1538+
id: AstId,
1539+
location: SourceLocation,
1540+
) -> AstNode {
1541+
let datatype = Box::new(datatype);
1542+
AstNode { stmt: AstStatement::DataTypeDeclaration(datatype), id, location }
1543+
}
15151544
}
15161545
#[derive(Debug, Clone, PartialEq)]
15171546
pub struct EmptyStatement {}
@@ -1594,3 +1623,10 @@ pub struct JumpStatement {
15941623
pub struct LabelStatement {
15951624
pub name: String,
15961625
}
1626+
1627+
/// Represents a new vaiable declaration in the body
1628+
#[derive(Clone, Debug, PartialEq)]
1629+
pub struct InlineVariable {
1630+
pub name: Box<AstNode>,
1631+
pub datatype: Option<Box<AstNode>>,
1632+
}

src/lexer/tests/lexer_tests.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,16 @@ fn undefined_pragmas_are_ignored_by_the_lexer() {
5858
#[test]
5959
fn registered_pragmas_parsed() {
6060
let mut lexer = lex(r"
61-
{external}{ref}{sized}{not_registerd}
61+
{external}{ref}{sized}{def}{not_registerd}
6262
");
6363
assert_eq!(lexer.token, PropertyExternal, "Token : {}", lexer.slice());
6464
lexer.advance();
6565
assert_eq!(lexer.token, PropertyByRef, "Token : {}", lexer.slice());
6666
lexer.advance();
6767
assert_eq!(lexer.token, PropertySized, "Token : {}", lexer.slice());
6868
lexer.advance();
69+
assert_eq!(lexer.token, PropertyDef, "Token : {}", lexer.slice());
70+
lexer.advance();
6971
assert_eq!(lexer.token, End);
7072
}
7173

src/lexer/tokens.rs

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ pub enum Token {
2222
#[token("{sized}")]
2323
PropertySized,
2424

25+
#[token("{def}")]
26+
PropertyDef,
27+
2528
#[token("PROGRAM", ignore(case))]
2629
KeywordProgram,
2730

src/parser.rs

+47-63
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ fn parse_super_class(lexer: &mut ParseSession) -> Option<String> {
338338
fn parse_return_type(lexer: &mut ParseSession, pou_type: &PouType) -> Option<DataTypeDeclaration> {
339339
let start_return_type = lexer.range().start;
340340
if lexer.try_consume(&KeywordColon) {
341-
if let Some((declaration, initializer)) = parse_data_type_definition(lexer, None) {
341+
if let Some((declaration, initializer)) = parse_datatype_with_initializer(lexer, None) {
342342
if let Some(init) = initializer {
343343
lexer.accept_diagnostic(Diagnostic::unexpected_initializer_on_function_return(
344344
init.get_location(),
@@ -587,7 +587,7 @@ fn parse_full_data_type_definition(
587587
None,
588588
))
589589
} else {
590-
parse_data_type_definition(lexer, name).map(|(type_def, initializer)| {
590+
parse_datatype_with_initializer(lexer, name).map(|(type_def, initializer)| {
591591
if lexer.try_consume(&KeywordDotDotDot) {
592592
(
593593
DataTypeDeclaration::DataTypeDefinition {
@@ -605,23 +605,29 @@ fn parse_full_data_type_definition(
605605
})
606606
}
607607

608-
// TYPE xxx : 'STRUCT' | '(' | IDENTIFIER
609-
fn parse_data_type_definition(
608+
fn parse_datatype_with_initializer(
610609
lexer: &mut ParseSession,
611610
name: Option<String>,
612611
) -> Option<DataTypeWithInitializer> {
612+
parse_data_type_definition(lexer, name).map(|type_def| {
613+
let initializer =
614+
if lexer.try_consume(&KeywordAssignment) { Some(parse_expression(lexer)) } else { None };
615+
616+
(type_def, initializer)
617+
})
618+
}
619+
620+
// TYPE xxx : 'STRUCT' | '(' | IDENTIFIER
621+
fn parse_data_type_definition(lexer: &mut ParseSession, name: Option<String>) -> Option<DataTypeDeclaration> {
613622
let start = lexer.location();
614623
if lexer.try_consume(&KeywordStruct) {
615624
// Parse struct
616625
let variables = parse_variable_list(lexer);
617-
Some((
618-
DataTypeDeclaration::DataTypeDefinition {
619-
data_type: DataType::StructType { name, variables },
620-
location: start.span(&lexer.location()),
621-
scope: lexer.scope.clone(),
622-
},
623-
None,
624-
))
626+
Some(DataTypeDeclaration::DataTypeDefinition {
627+
data_type: DataType::StructType { name, variables },
628+
location: start.span(&lexer.location()),
629+
scope: lexer.scope.clone(),
630+
})
625631
} else if lexer.try_consume(&KeywordArray) {
626632
parse_array_type_definition(lexer, name)
627633
} else if lexer.try_consume(&KeywordPointer) {
@@ -661,23 +667,18 @@ fn parse_pointer_definition(
661667
lexer: &mut ParseSession,
662668
name: Option<String>,
663669
start_pos: usize,
664-
) -> Option<(DataTypeDeclaration, Option<AstNode>)> {
665-
parse_data_type_definition(lexer, None).map(|(decl, initializer)| {
666-
(
667-
DataTypeDeclaration::DataTypeDefinition {
668-
data_type: DataType::PointerType { name, referenced_type: Box::new(decl) },
669-
location: lexer.source_range_factory.create_range(start_pos..lexer.last_range.end),
670-
scope: lexer.scope.clone(),
671-
},
672-
initializer,
673-
)
670+
) -> Option<DataTypeDeclaration> {
671+
parse_data_type_definition(lexer, None).map(|decl| DataTypeDeclaration::DataTypeDefinition {
672+
data_type: DataType::PointerType { name, referenced_type: Box::new(decl) },
673+
location: lexer.source_range_factory.create_range(start_pos..lexer.last_range.end),
674+
scope: lexer.scope.clone(),
674675
})
675676
}
676677

677678
fn parse_type_reference_type_definition(
678679
lexer: &mut ParseSession,
679680
name: Option<String>,
680-
) -> Option<(DataTypeDeclaration, Option<AstNode>)> {
681+
) -> Option<DataTypeDeclaration> {
681682
let start = lexer.range().start;
682683
//Subrange
683684
let referenced_type = lexer.slice_and_advance();
@@ -692,9 +693,6 @@ fn parse_type_reference_type_definition(
692693
None
693694
};
694695

695-
let initial_value =
696-
if lexer.try_consume(&KeywordAssignment) { Some(parse_expression(lexer)) } else { None };
697-
698696
let end = lexer.last_range.end;
699697
if name.is_some() || bounds.is_some() {
700698
let data_type = match bounds {
@@ -732,15 +730,12 @@ fn parse_type_reference_type_definition(
732730
scope: lexer.scope.clone(),
733731
},
734732
};
735-
Some((data_type, initial_value))
733+
Some(data_type)
736734
} else {
737-
Some((
738-
DataTypeDeclaration::DataTypeReference {
739-
referenced_type,
740-
location: lexer.source_range_factory.create_range(start..end),
741-
},
742-
initial_value,
743-
))
735+
Some(DataTypeDeclaration::DataTypeReference {
736+
referenced_type,
737+
location: lexer.source_range_factory.create_range(start..end),
738+
})
744739
}
745740
}
746741

@@ -778,7 +773,7 @@ fn parse_string_size_expression(lexer: &mut ParseSession) -> Option<AstNode> {
778773
fn parse_string_type_definition(
779774
lexer: &mut ParseSession,
780775
name: Option<String>,
781-
) -> Option<(DataTypeDeclaration, Option<AstNode>)> {
776+
) -> Option<DataTypeDeclaration> {
782777
let text = lexer.slice().to_string();
783778
let start = lexer.range().start;
784779
let is_wide = lexer.token == KeywordWideString;
@@ -805,34 +800,26 @@ fn parse_string_type_definition(
805800
}),
806801
_ => Some(DataTypeDeclaration::DataTypeReference { referenced_type: text, location }),
807802
}
808-
.zip(Some(lexer.try_consume(&KeywordAssignment).then(|| parse_expression(lexer))))
809803
}
810804

811-
fn parse_enum_type_definition(
812-
lexer: &mut ParseSession,
813-
name: Option<String>,
814-
) -> Option<(DataTypeDeclaration, Option<AstNode>)> {
805+
fn parse_enum_type_definition(lexer: &mut ParseSession, name: Option<String>) -> Option<DataTypeDeclaration> {
815806
let start = lexer.last_location();
816807
let elements = parse_any_in_region(lexer, vec![KeywordParensClose], |lexer| {
817808
// Parse Enum - we expect at least one element
818809
let elements = parse_expression_list(lexer);
819810
Some(elements)
820811
})?;
821-
let initializer = lexer.try_consume(&KeywordAssignment).then(|| parse_expression(lexer));
822-
Some((
823-
DataTypeDeclaration::DataTypeDefinition {
824-
data_type: DataType::EnumType { name, elements, numeric_type: DINT_TYPE.to_string() },
825-
location: start.span(&lexer.last_location()),
826-
scope: lexer.scope.clone(),
827-
},
828-
initializer,
829-
))
812+
Some(DataTypeDeclaration::DataTypeDefinition {
813+
data_type: DataType::EnumType { name, elements, numeric_type: DINT_TYPE.to_string() },
814+
location: start.span(&lexer.last_location()),
815+
scope: lexer.scope.clone(),
816+
})
830817
}
831818

832819
fn parse_array_type_definition(
833820
lexer: &mut ParseSession,
834821
name: Option<String>,
835-
) -> Option<(DataTypeDeclaration, Option<AstNode>)> {
822+
) -> Option<DataTypeDeclaration> {
836823
let start = lexer.last_range.start;
837824
let range = parse_any_in_region(lexer, vec![KeywordOf], |lexer| {
838825
// Parse Array range
@@ -849,7 +836,7 @@ fn parse_array_type_definition(
849836
})?;
850837

851838
let inner_type_defintion = parse_data_type_definition(lexer, None);
852-
inner_type_defintion.map(|(reference, initializer)| {
839+
inner_type_defintion.map(|reference| {
853840
let reference_end = reference.get_location().to_range().map(|it| it.end).unwrap_or(0);
854841
let location = lexer.source_range_factory.create_range(start..reference_end);
855842

@@ -876,19 +863,16 @@ fn parse_array_type_definition(
876863
}
877864
};
878865

879-
(
880-
DataTypeDeclaration::DataTypeDefinition {
881-
data_type: DataType::ArrayType {
882-
name,
883-
bounds: range,
884-
referenced_type: Box::new(reference),
885-
is_variable_length,
886-
},
887-
location,
888-
scope: lexer.scope.clone(),
866+
DataTypeDeclaration::DataTypeDefinition {
867+
data_type: DataType::ArrayType {
868+
name,
869+
bounds: range,
870+
referenced_type: Box::new(reference),
871+
is_variable_length,
889872
},
890-
initializer,
891-
)
873+
location,
874+
scope: lexer.scope.clone(),
875+
}
892876
})
893877
}
894878

src/parser/expressions_parser.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use plc_source::source_location::SourceLocation;
1515
use regex::{Captures, Regex};
1616
use std::{ops::Range, str::FromStr};
1717

18-
use super::parse_hardware_access;
18+
use super::{parse_data_type_definition, parse_hardware_access};
1919

2020
macro_rules! parse_left_associative_expression {
2121
($lexer: expr, $action : expr,
@@ -281,6 +281,12 @@ fn parse_atomic_leaf_expression(lexer: &mut ParseSession<'_>) -> Result<AstNode,
281281
LiteralNull => parse_null_literal(lexer),
282282
KeywordSquareParensOpen => parse_array_literal(lexer),
283283
DirectAccess(access) => parse_direct_access(lexer, access),
284+
PropertyDef => {
285+
//Just consume the {def} and go further, if it's a variable we parse it next
286+
lexer.advance();
287+
parse_atomic_leaf_expression(lexer)
288+
}
289+
KeywordVar => parse_inline_declaration(lexer),
284290
_ => {
285291
if lexer.closing_keywords.contains(&vec![KeywordParensClose])
286292
&& matches!(lexer.last_token, KeywordOutputAssignment | KeywordAssignment)
@@ -296,6 +302,27 @@ fn parse_atomic_leaf_expression(lexer: &mut ParseSession<'_>) -> Result<AstNode,
296302
}
297303
}
298304

305+
fn parse_inline_declaration(lexer: &mut ParseSession<'_>) -> Result<AstNode, Diagnostic> {
306+
//Consume the direct access
307+
let location = lexer.location();
308+
//Inline variable declaration
309+
lexer.advance();
310+
//Parse the name
311+
let name = parse_identifier(lexer);
312+
let datatype = if lexer.try_consume(&KeywordColon) {
313+
//Parse datatype
314+
let type_location = lexer.location();
315+
parse_data_type_definition(lexer, None).map(|it| {
316+
AstFactory::create_type_declaration(it, lexer.next_id(), type_location.span(&lexer.location()))
317+
})
318+
} else {
319+
None
320+
};
321+
let location = location.span(&lexer.last_location());
322+
323+
Ok(AstFactory::create_inline_declaration(name, datatype, lexer.next_id(), location))
324+
}
325+
299326
fn parse_identifier(lexer: &mut ParseSession<'_>) -> AstNode {
300327
AstFactory::create_identifier(&lexer.slice_and_advance(), &lexer.last_location(), lexer.next_id())
301328
}

src/parser/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod control_parser_tests;
1111
mod expressions_parser_tests;
1212
mod function_parser_tests;
1313
mod initializer_parser_tests;
14+
mod inline_variable_tests;
1415
mod misc_parser_tests;
1516
mod parse_errors;
1617
mod parse_generics;

0 commit comments

Comments
 (0)