Skip to content

Commit d27a846

Browse files
committed
add helper methods to create pipelines with custom particitants
1 parent 8c29807 commit d27a846

File tree

7 files changed

+174
-42
lines changed

7 files changed

+174
-42
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/plc_driver/src/lib.rs

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ pub fn compile_with_pipeline<T: SourceContainer + Clone + 'static>(
156156
mut pipeline: BuildPipeline<T>,
157157
) -> Result<()> {
158158
//register participants
159-
pipeline.register_default_participants();
159+
pipeline.register_default_mut_participants();
160160
let target = pipeline.compile_parameters.as_ref().and_then(|it| it.target.clone()).unwrap_or_default();
161161
let codegen_participant = CodegenParticipant {
162162
compile_options: pipeline.get_compile_options().unwrap(),
@@ -192,35 +192,56 @@ pub fn parse_and_annotate<T: SourceContainer + Clone>(
192192
name: &str,
193193
src: Vec<T>,
194194
) -> Result<(GlobalContext, AnnotatedProject), Diagnostic> {
195-
let (pipeline, project) = parse_and_annotate_with_diagnostics(name, src, Diagnostician::buffered())
196-
.map_err(|it| Diagnostic::new(it.buffer().unwrap_or_default()))?;
195+
let (pipeline, project) = parse_and_annotate_with_diagnostics(name, src, Diagnostician::buffered())?;
197196
Ok((pipeline.context, project))
198197
}
199198

199+
impl<T: SourceContainer> BuildPipeline<T> {
200+
pub fn from_sources(name: &str, src: Vec<T>, diagnostician: Diagnostician) -> Result<Self, Diagnostic> {
201+
// Parse the source to ast
202+
let project = Project::new(name.to_string()).with_sources(src);
203+
let Ok(context) = GlobalContext::new().with_source(project.get_sources(), None) else {
204+
return Err(Diagnostic::new(diagnostician.buffer().unwrap_or_default()));
205+
};
206+
let pipeline = BuildPipeline {
207+
context,
208+
project,
209+
diagnostician,
210+
compile_parameters: None,
211+
linker: LinkerType::Internal,
212+
mutable_participants: Vec::default(),
213+
participants: Vec::default(),
214+
module_name: Some("<internal>".to_string()),
215+
};
216+
Ok(pipeline)
217+
}
218+
219+
/// Parses, indexes and annotates the project, returning any diagnostics found along the way
220+
/// Used for tests where we don't want to run the full pipeline
221+
pub fn parse_and_annotate(&mut self) -> Result<AnnotatedProject, Diagnostic> {
222+
let Ok(project) = self.parse() else {
223+
return Err(Diagnostic::new(self.diagnostician.buffer().unwrap_or_default()));
224+
};
225+
let Ok(project) = self.index(project) else {
226+
return Err(Diagnostic::new(self.diagnostician.buffer().unwrap_or_default()));
227+
};
228+
let Ok(project) = self.annotate(project) else {
229+
return Err(Diagnostic::new(self.diagnostician.buffer().unwrap_or_default()));
230+
};
231+
Ok(project)
232+
}
233+
}
234+
200235
pub fn parse_and_annotate_with_diagnostics<T: SourceContainer + Clone>(
201236
name: &str,
202237
src: Vec<T>,
203238
diagnostician: Diagnostician,
204-
) -> Result<(BuildPipeline<T>, AnnotatedProject), Diagnostician> {
239+
) -> Result<(BuildPipeline<T>, AnnotatedProject), Diagnostic> {
205240
// Parse the source to ast
206-
let project = Project::new(name.to_string()).with_sources(src);
207-
let Ok(context) = GlobalContext::new().with_source(project.get_sources(), None) else {
208-
return Err(diagnostician);
209-
};
210-
let mut pipeline = BuildPipeline {
211-
context,
212-
project,
213-
diagnostician,
214-
compile_parameters: None,
215-
linker: LinkerType::Internal,
216-
mutable_participants: Vec::default(),
217-
participants: Vec::default(),
218-
module_name: Some("<internal>".to_string()),
219-
};
220-
pipeline.register_default_participants();
221-
let Ok(project) = pipeline.parse() else { return Err(pipeline.diagnostician) };
222-
let Ok(project) = pipeline.index(project) else { return Err(pipeline.diagnostician) };
223-
let Ok(project) = pipeline.annotate(project) else { return Err(pipeline.diagnostician) };
241+
let mut pipeline = BuildPipeline::from_sources(name, src, diagnostician)?;
242+
pipeline.register_default_mut_participants();
243+
let project = pipeline.parse_and_annotate()?;
244+
let _ = project.validate(&pipeline.context, &mut pipeline.diagnostician);
224245
Ok((pipeline, project))
225246
}
226247

@@ -230,7 +251,7 @@ pub fn parse_and_validate<T: SourceContainer + Clone>(name: &str, src: Vec<T>) -
230251
let _ = project.validate(&pipeline.context, &mut pipeline.diagnostician);
231252
pipeline.diagnostician.buffer().unwrap()
232253
}
233-
Err(diagnostician) => diagnostician.buffer().unwrap(),
254+
Err(diagnostic) => diagnostic.to_string(),
234255
}
235256
}
236257

@@ -266,7 +287,7 @@ fn generate_to_string_internal<T: SourceContainer>(
266287
participants: Vec::default(),
267288
module_name: Some("<internal>".to_string()),
268289
};
269-
pipeline.register_default_participants();
290+
pipeline.register_default_mut_participants();
270291
let project = pipeline.parse()?;
271292
let project = pipeline.index(project)?;
272293
let project = pipeline.annotate(project)?;

compiler/plc_driver/src/pipelines.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,21 +247,28 @@ impl<T: SourceContainer> BuildPipeline<T> {
247247
log::info!("{err}")
248248
}
249249
}
250-
/// Register all default participants (excluding codegen/linking)
251-
pub fn register_default_participants(&mut self) {
250+
251+
pub fn get_default_mut_particitants(&self) -> Vec<Box<dyn PipelineParticipantMut>> {
252252
use participant::InitParticipant;
253253

254-
// XXX: should we use a static array of participants?
255-
let mut_participants: Vec<Box<dyn PipelineParticipantMut>> = vec![
254+
vec![
256255
Box::new(VirtualTableGenerator::new(self.context.provider())),
257256
Box::new(PolymorphicCallLowerer::new(self.context.provider())),
258257
Box::new(PropertyLowerer::new(self.context.provider())),
259258
Box::new(InitParticipant::new(self.project.get_init_symbol_name(), self.context.provider())),
260259
Box::new(AggregateTypeLowerer::new(self.context.provider())),
261260
Box::new(InheritanceLowerer::new(self.context.provider())),
262-
];
261+
]
262+
}
263+
/// Register all default participants (excluding codegen/linking)
264+
pub fn register_default_mut_participants(&mut self) {
265+
// XXX: should we use a static array of participants?
266+
let mut_participants = self.get_default_mut_particitants();
267+
self.register_mut_participants(mut_participants);
268+
}
263269

264-
for participant in mut_participants {
270+
pub fn register_mut_participants(&mut self, participants: Vec<Box<dyn PipelineParticipantMut>>) {
271+
for participant in participants {
265272
self.register_mut_participant(participant)
266273
}
267274
}

compiler/plc_driver/src/runner.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub fn compile<T: Compilable>(codegen_context: &CodegenContext, source: T) -> Ge
4343
module_name: Some("<internal>".to_string()),
4444
};
4545

46-
pipeline.register_default_participants();
46+
pipeline.register_default_mut_participants();
4747

4848
let project = pipeline.parse().unwrap();
4949
let project = pipeline.index(project).unwrap();

compiler/plc_lowering/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ edition = "2021"
77
plc = { path = "../..", package = "rusty" }
88
plc_ast = { path = "../plc_ast" }
99
plc_source = { path = "../plc_source" }
10+
plc_diagnostics = { path = "../plc_diagnostics" }
1011
log.workspace = true
1112

1213
[dev-dependencies]
1314
plc_driver = { path = "../plc_driver" }
14-
insta = "1.31.0"
15+
insta = "1.31.0"

compiler/plc_lowering/src/initializer.rs

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use plc::index::{FxIndexMap, Index};
2828
use plc_ast::{ast::AstNode, visitor::AstVisitor};
2929

30+
#[derive(Debug)]
3031
enum Body {
3132
Internal(Vec<AstNode>),
3233
External,
@@ -44,12 +45,6 @@ pub struct Initializer<'idx> {
4445
impl AstVisitor for Initializer<'_> {
4546
fn visit_compilation_unit(&mut self, unit: &plc_ast::ast::CompilationUnit) {
4647
// Read all structs and POU structs, collect their implicit initializers if available
47-
unit.pous.iter().for_each(|pou| {
48-
// find the pou index entry
49-
if let Some(pie) = self.index.find_pou_type(&pou.name) {
50-
pie.initial_value
51-
}
52-
});
5348
// Add a call to the constructor to memcpy the imlicit initializer
5449
// For each of the call statement or reference in the pou initializer, add an assignment to
5550
// the constructor
@@ -68,14 +63,121 @@ impl Initializer<'_> {
6863
}
6964

7065
mod tests {
66+
use plc_ast::visitor::AstVisitor;
67+
use plc_diagnostics::diagnostician::Diagnostician;
68+
7169
#[test]
72-
fn struct_gets_imlicit_initializer_and_constructor() {}
70+
fn struct_gets_imlicit_initializer_and_constructor() {
71+
let src = r#"
72+
TYPE MyStruct : STRUCT
73+
a : INT := 5;
74+
b : REAL := 3.14;
75+
c : BOOL := TRUE;
76+
END_STRUCT
77+
"#;
78+
79+
let diagnostician = Diagnostician::buffered();
80+
let mut pipeline =
81+
plc_driver::pipelines::BuildPipeline::from_sources("test.st", vec![(src)], diagnostician)
82+
.unwrap();
83+
let project = pipeline.parse_and_annotate().unwrap();
84+
// Visit the AST with the Initializer
85+
let mut initializer = super::Initializer::new(&project.index);
86+
for unit in &project.units {
87+
initializer.visit_compilation_unit(unit.get_unit());
88+
}
89+
// Check for implicit initializer
90+
// Expecting a variable declaration: MyStruct MyStruct_init = {5, 3.14, TRUE};
91+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("MyStruct"), @r#""#);
92+
// Check for an empty constructor
93+
// Expecting a function declaration: void MyStruct_ctor(MyStruct* self)
94+
// Expecting a call to memcpy inside the constructor: memcpy(self, &MyStruct_init,
95+
// sizeof(MyStruct));
96+
insta::assert_debug_snapshot!(initializer.constructors.get("MyStruct"), @r#""#);
97+
}
7398

7499
#[test]
75-
fn struct_gets_constructor() {}
100+
fn nested_structs_get_initializers_and_constructors() {
101+
let src = r#"
102+
TYPE InnerStruct : STRUCT
103+
x : INT := 10;
104+
y : INT := 20;
105+
END_STRUCT
106+
107+
TYPE OuterStruct : STRUCT
108+
inner : InnerStruct;
109+
z : REAL := 2.71;
110+
END_STRUCT
111+
"#;
112+
113+
let diagnostician = Diagnostician::buffered();
114+
let mut pipeline =
115+
plc_driver::pipelines::BuildPipeline::from_sources("test.st", vec![(src)], diagnostician)
116+
.unwrap();
117+
let project = pipeline.parse_and_annotate().unwrap();
118+
// Visit the AST with the Initializer
119+
let mut initializer = super::Initializer::new(&project.index);
120+
for unit in &project.units {
121+
initializer.visit_compilation_unit(unit.get_unit());
122+
}
123+
// Check for implicit initializers
124+
// Expecting a variable declaration: InnerStruct InnerStruct_init = {10, 20};
125+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("InnerStruct"), @r#""#);
126+
// Expecting a variable declaration: OuterStruct OuterStruct_init = {0, 0, 2.71};
127+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("OuterStruct"), @r#""#);
128+
// Check for constructors
129+
// Expecting a function declaration: void InnerStruct_ctor(InnerStruct* self)
130+
// Expecting a call to memcpy inside the constructor: memcpy(self, &InnerStruct_init,
131+
// sizeof(InnerStruct));
132+
insta::assert_debug_snapshot!(initializer.constructors.get("InnerStruct"), @r#""#);
133+
// Expecting a function declaration: void OuterStruct_ctor(OuterStruct* self)
134+
// Expecting a call to memcpy inside the constructor: memcpy(self, &OuterStruct_init,
135+
// sizeof(OuterStruct));
136+
// Expecting a call to InnerStruct_ctor(&self->inner);
137+
insta::assert_debug_snapshot!(initializer.constructors.get("OuterStruct"), @r#""#);
138+
}
76139

77140
#[test]
78-
fn nested_structs_get_initializers_and_constructors() {}
141+
fn nested_structs_with_values_get_initializers_and_constructors() {
142+
let src = r#"
143+
TYPE InnerStruct : STRUCT
144+
x : INT := 10;
145+
y : INT := 20;
146+
END_STRUCT
147+
148+
TYPE OuterStruct : STRUCT
149+
inner : InnerStruct := (x := 1, y := 2);
150+
z : REAL := 2.71;
151+
END_STRUCT
152+
"#;
153+
154+
let diagnostician = Diagnostician::buffered();
155+
let mut pipeline =
156+
plc_driver::pipelines::BuildPipeline::from_sources("test.st", vec![(src)], diagnostician)
157+
.unwrap();
158+
let project = pipeline.parse_and_annotate().unwrap();
159+
// Visit the AST with the Initializer
160+
let mut initializer = super::Initializer::new(&project.index);
161+
for unit in &project.units {
162+
initializer.visit_compilation_unit(unit.get_unit());
163+
}
164+
// Check for implicit initializers
165+
// Expecting a variable declaration: InnerStruct InnerStruct_init = {10, 20};
166+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("InnerStruct"), @r#""#);
167+
// Expecting a variable declaration: OuterStruct OuterStruct_init = {1, 2, 2.71};
168+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("OuterStruct"), @r#""#);
169+
// Check for constructors
170+
// Expecting a function declaration: void InnerStruct_ctor(InnerStruct* self)
171+
// Expecting a call to memcpy inside the constructor: memcpy(self, &InnerStruct_init,
172+
// sizeof(InnerStruct));
173+
insta::assert_debug_snapshot!(initializer.constructors.get("InnerStruct"), @r#""#);
174+
// Expecting a function declaration: void OuterStruct_ctor(OuterStruct* self)
175+
// Expecting a call to memcpy inside the constructor: memcpy(self, &OuterStruct_init,
176+
// sizeof(OuterStruct));
177+
// TODO: how do we prevent this from overriding the set values?
178+
// Expecting a call to InnerStruct_ctor(&self->inner);
179+
insta::assert_debug_snapshot!(initializer.constructors.get("OuterStruct"), @r#""#);
180+
}
79181

80182
#[test]
81183
fn variable_with_pointer_initializer_is_added_to_constructor() {}

tests/test_utils/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub fn parse_and_validate_buffered_ast(src: &str) -> Vec<CompilationUnit> {
3737
project.validate(&pipeline.context, &mut pipeline.diagnostician).unwrap();
3838
project.units.into_iter().map(CompilationUnit::from).collect()
3939
}
40-
Err(diagnostician) => panic!("{}", diagnostician.buffer().unwrap()),
40+
Err(diagnostics) => panic!("{diagnostics}"),
4141
}
4242
}
4343

0 commit comments

Comments
 (0)