Skip to content

Commit 171e617

Browse files
committed
wip: lowered ast
1 parent d27a846 commit 171e617

File tree

3 files changed

+295
-6
lines changed

3 files changed

+295
-6
lines changed

compiler/plc_lowering/src/initializer.rs

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

30-
#[derive(Debug)]
30+
#[derive(Debug, PartialEq)]
3131
enum Body {
3232
Internal(Vec<AstNode>),
3333
External,
@@ -147,6 +147,7 @@ mod tests {
147147
148148
TYPE OuterStruct : STRUCT
149149
inner : InnerStruct := (x := 1, y := 2);
150+
inner2 : InnerStruct := (y := 3);
150151
z : REAL := 2.71;
151152
END_STRUCT
152153
"#;
@@ -164,7 +165,7 @@ mod tests {
164165
// Check for implicit initializers
165166
// Expecting a variable declaration: InnerStruct InnerStruct_init = {10, 20};
166167
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("InnerStruct"), @r#""#);
167-
// Expecting a variable declaration: OuterStruct OuterStruct_init = {1, 2, 2.71};
168+
// Expecting a variable declaration: OuterStruct OuterStruct_init = {10, 20, 10, 20, 2.71};
168169
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("OuterStruct"), @r#""#);
169170
// Check for constructors
170171
// Expecting a function declaration: void InnerStruct_ctor(InnerStruct* self)
@@ -174,17 +175,270 @@ mod tests {
174175
// Expecting a function declaration: void OuterStruct_ctor(OuterStruct* self)
175176
// Expecting a call to memcpy inside the constructor: memcpy(self, &OuterStruct_init,
176177
// sizeof(OuterStruct));
177-
// TODO: how do we prevent this from overriding the set values?
178178
// Expecting a call to InnerStruct_ctor(&self->inner);
179+
// Expecting a call to InnerStruct_ctor(&self->inner2);
180+
// Expecting assignments: self->inner.x = 1; self->inner.y = 2;
181+
// Expecting assignments: self->inner2.y = 3;
179182
insta::assert_debug_snapshot!(initializer.constructors.get("OuterStruct"), @r#""#);
180183
}
181184

182185
#[test]
183-
fn variable_with_pointer_initializer_is_added_to_constructor() {}
186+
fn variable_with_pointer_initializer_is_added_to_constructor() {
187+
let src = r#"
188+
VAR_GLOBAL
189+
gVar : INT;
190+
myStructVar : MyStruct;
191+
END_VAR
192+
TYPE MyStruct : STRUCT
193+
a : INT := 5;
194+
b : POINTER TO INT := ADR(gVar);
195+
c : BOOL := TRUE;
196+
END_STRUCT
197+
"#;
198+
199+
let diagnostician = Diagnostician::buffered();
200+
let mut pipeline =
201+
plc_driver::pipelines::BuildPipeline::from_sources("test.st", vec![(src)], diagnostician)
202+
.unwrap();
203+
let project = pipeline.parse_and_annotate().unwrap();
204+
// Visit the AST with the Initializer
205+
let mut initializer = super::Initializer::new(&project.index);
206+
for unit in &project.units {
207+
initializer.visit_compilation_unit(unit.get_unit());
208+
}
209+
// Check for implicit initializers
210+
// Expecting a variable declaration: MyStruct MyStruct_init = {5, 0, 1};
211+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("MyStruct"), @r#""#);
212+
// Check for constructor
213+
// Expecting a function declaration: void MyStruct_ctor(MyStruct* self)
214+
// Expecting a call to memcpy inside the constructor: memcpy(self, &MyStruct_init,
215+
// sizeof(MyStruct));
216+
// Expecting an assignment: self->b = &gVar;
217+
insta::assert_debug_snapshot!(initializer.constructors.get("MyStruct"), @r#""#);
218+
}
184219

185220
#[test]
186-
fn enum_default_values_in_struct() {}
221+
fn enum_default_values_in_struct() {
222+
let src = r#"
223+
TYPE MyEnum : (Option1, Option2, Option3) := Option3;
224+
TYPE MyStruct : STRUCT
225+
e : MyEnum := Option2;
226+
n : INT := 42;
227+
END_STRUCT
228+
"#;
229+
230+
let diagnostician = Diagnostician::buffered();
231+
let mut pipeline =
232+
plc_driver::pipelines::BuildPipeline::from_sources("test.st", vec![(src)], diagnostician)
233+
.unwrap();
234+
let project = pipeline.parse_and_annotate().unwrap();
235+
// Visit the AST with the Initializer
236+
let mut initializer = super::Initializer::new(&project.index);
237+
for unit in &project.units {
238+
initializer.visit_compilation_unit(unit.get_unit());
239+
}
240+
// Check for implicit initializer
241+
// Expecting a variable declaration: MyEnum MyEnum_init = 2;
242+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("MyEnum"), @r#""#);
243+
// Expecting a function declaration: void MyEnum_ctor(MyEnum* self)
244+
// Expecting an assignment inside the constructor: *self = MyEnum_init;
245+
insta::assert_debug_snapshot!(initializer.constructors.get("MyEnum"), @r#""#);
246+
// Expecting a variable declaration: MyStruct MyStruct_init = {3, 42};
247+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("MyStruct"), @r#""#);
248+
// Expecting a function declaration: void MyStruct_ctor(MyStruct* self)
249+
// Expecting a call to memcpy inside the constructor: memcpy(self, &MyStruct_init,
250+
// sizeof(MyStruct));
251+
// Expecting a call to MyEnum_ctor(&self->e);
252+
// Expecting an assignment: self->e = 1;
253+
insta::assert_debug_snapshot!(initializer.constructors.get("MyStruct"), @r#""#);
254+
}
187255

188256
#[test]
189-
fn nested_struct_with_different_default_values() {}
257+
fn nested_struct_with_different_default_values() {
258+
let src = r#"
259+
TYPE InnerStruct : STRUCT
260+
a : INT := 1;
261+
b : POINTER TO INT := ADR(a);
262+
END_STRUCT
263+
264+
TYPE InnerStruct2 : STRUCT
265+
c : INT := 4;
266+
d : INT := 5;
267+
inner : InnerStruct := (a := 6);
268+
inner2 : InnerStruct := (b := ADR(c));
269+
END_STRUCT
270+
271+
TYPE OuterStruct : STRUCT
272+
e : INT := 0;
273+
inner : InnerStruct2 := (a := 1, b := 2, inner := (a := 3));
274+
inner2 : InnerStruct2 := (d := 8, inner := (b := ADR(e)));
275+
inner3 : InnerStruct2 := (inner (a := 9));
276+
END_STRUCT
277+
"#;
278+
279+
let diagnostician = Diagnostician::buffered();
280+
let mut pipeline =
281+
plc_driver::pipelines::BuildPipeline::from_sources("test.st", vec![(src)], diagnostician)
282+
.unwrap();
283+
let project = pipeline.parse_and_annotate().unwrap();
284+
// Visit the AST with the Initializer
285+
let mut initializer = super::Initializer::new(&project.index);
286+
for unit in &project.units {
287+
initializer.visit_compilation_unit(unit.get_unit());
288+
}
289+
// Check for implicit initializers
290+
// Expecting a variable declaration: InnerStruct InnerStruct_init = {1, 0};
291+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("InnerStruct"), @r#""#);
292+
// Expecting a variable declaration: InnerStruct InnerStruct_init = {4, 5, 1, 0, 1, 0};
293+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("InnerStruct2"), @r#""#);
294+
// Expecting a variable declaration: OuterStruct OuterStruct_init = {0, 4, 5, 1, 0, 1, 0,
295+
// 4, 5, 1, 0, 1, 0, 4, 5, 1, 0, 1, 0};
296+
insta::assert_debug_snapshot!(initializer.implicit_initializers.get("OuterStruct"), @r#""#);
297+
// Check for constructors
298+
// Expecting a function declaration: void InnerStruct_ctor(InnerStruct* self)
299+
// Expecting a call to memcpy inside the constructor: memcpy(self, &InnerStruct_init,
300+
// sizeof(InnerStruct));
301+
// Expecting an assignment: self->b = &self->a;
302+
insta::assert_debug_snapshot!(initializer.constructors.get("InnerStruct"), @r#""#);
303+
// Expecting a function declaration: void InnerStruct2_ctor(InnerStruct2* self)
304+
// Expecting a call to memcpy inside the constructor: memcpy(self, &InnerStruct_init,
305+
// sizeof(InnerStruct));
306+
// Expecting a call to InnerStruct_ctor(&self->inner);
307+
// Expecting a call to InnerStruct_ctor(&self->inner2);
308+
// Expecting assignments: self->inner.a = 6; self->inner2.b = &self->c;
309+
insta::assert_debug_snapshot!(initializer.constructors.get("InnerStruct2"), @r#""#);
310+
// Expecting a function declaration: void OuterStruct_ctor(OuterStruct* self)
311+
// Expecting a call to memcpy inside the constructor: memcpy(self, &OuterStruct_init,
312+
// sizeof(OuterStruct));
313+
// Expecting a call to InnerStruct2_ctor(&self->inner);
314+
// Expecting a call to InnerStruct2_ctor(&self->inner2);
315+
// Expecting a call to InnerStruct2_ctor(&self->inner3);
316+
// Expecting assignments: self->inner.a = 1; self->inner.b = 2; self->inner.inner.a = 3;
317+
// Expecting assignments: self->inner2.d = 8; self->inner2.inner.b = &self->e;
318+
// Expecting assignments: self->inner3.inner.a = 9;
319+
insta::assert_debug_snapshot!(initializer.constructors.get("OuterStruct"), @r#""#);
320+
}
321+
322+
#[test]
323+
fn global_constructor_is_generated() {
324+
let src = r#"
325+
VAR_GLOBAL
326+
gVar1 : INT := 10;
327+
gVar2 : REAL;
328+
gStructVar : MyStruct;
329+
END_VAR
330+
331+
TYPE MyStruct : STRUCT
332+
a : INT := 5;
333+
b : POINTER TO INT := ADR(gVar1);
334+
c : BOOL := TRUE;
335+
END_STRUCT
336+
"#;
337+
338+
let diagnostician = Diagnostician::buffered();
339+
let mut pipeline =
340+
plc_driver::pipelines::BuildPipeline::from_sources("test.st", vec![(src)], diagnostician)
341+
.unwrap();
342+
let project = pipeline.parse_and_annotate().unwrap();
343+
// Visit the AST with the Initializer
344+
let mut initializer = super::Initializer::new(&project.index);
345+
for unit in &project.units {
346+
initializer.visit_compilation_unit(unit.get_unit());
347+
}
348+
// Check for global constructor
349+
// Expecting a call to MyStruct_ctor(&gStructVar);
350+
insta::assert_debug_snapshot!(initializer.global_constructor, @r#""#);
351+
}
352+
353+
#[test]
354+
fn function_constructors_have_the_function_name() {
355+
let src = r#"
356+
FUNCTION MyFunction : INT
357+
VAR
358+
localStruct : MyStruct;
359+
END_VAR
360+
END_FUNCTION
361+
362+
TYPE MyStruct : STRUCT
363+
a : INT := 5;
364+
b : BOOL := TRUE;
365+
END_STRUCT
366+
"#;
367+
368+
let diagnostician = Diagnostician::buffered();
369+
let mut pipeline =
370+
plc_driver::pipelines::BuildPipeline::from_sources("test.st", vec![(src)], diagnostician)
371+
.unwrap();
372+
let project = pipeline.parse_and_annotate().unwrap();
373+
// Visit the AST with the Initializer
374+
let mut initializer = super::Initializer::new(&project.index);
375+
for unit in &project.units {
376+
initializer.visit_compilation_unit(unit.get_unit());
377+
}
378+
// Check for function constructor
379+
// Expecting a call to MyStruct_ctor(&localStruct);
380+
insta::assert_debug_snapshot!(initializer.constructors.get("MyFunction"), @r#""#);
381+
}
382+
383+
#[test]
384+
fn programs_are_globals() {
385+
let src = r#"
386+
PROGRAM MyProgram
387+
VAR
388+
END_VAR
389+
END_PROGRAM
390+
"#;
391+
392+
let diagnostician = Diagnostician::buffered();
393+
let mut pipeline =
394+
plc_driver::pipelines::BuildPipeline::from_sources("test.st", vec![(src)], diagnostician)
395+
.unwrap();
396+
let project = pipeline.parse_and_annotate().unwrap();
397+
// Visit the AST with the Initializer
398+
let mut initializer = super::Initializer::new(&project.index);
399+
for unit in &project.units {
400+
initializer.visit_compilation_unit(unit.get_unit());
401+
}
402+
// Check for program constructor
403+
// Expecting a call to MyProgram_ctor(&progStruct);
404+
insta::assert_debug_snapshot!(initializer.global_constructor, @r#""#);
405+
}
406+
407+
#[test]
408+
fn external_structs_and_variables_are_not_initialized() {
409+
let src = r#"
410+
VAR_GLOBAL
411+
internalVar : MyExtStruct;
412+
END_VAR
413+
414+
{external}
415+
VAR_GLOBAL
416+
extVar : MyExtStruct;
417+
END_VAR
418+
419+
{external}
420+
TYPE MyExtStruct : STRUCT
421+
a : INT := 5;
422+
b : BOOL := TRUE;
423+
END_STRUCT
424+
"#;
425+
426+
let diagnostician = Diagnostician::buffered();
427+
let mut pipeline =
428+
plc_driver::pipelines::BuildPipeline::from_sources("test.st", vec![(src)], diagnostician)
429+
.unwrap();
430+
let project = pipeline.parse_and_annotate().unwrap();
431+
// Visit the AST with the Initializer
432+
let mut initializer = super::Initializer::new(&project.index);
433+
for unit in &project.units {
434+
initializer.visit_compilation_unit(unit.get_unit());
435+
}
436+
// Check for internal var assignment in global constructor
437+
// Expecting a call to MyExtStruct_ctor(&internalVar);
438+
// No call to MyExtStruct_ctor(&extVar);
439+
insta::assert_debug_snapshot!(initializer.global_constructor, @r#""#);
440+
// Check that no implicit initializer or constructor is generated for MyExtStruct
441+
assert_eq!(initializer.implicit_initializers.get("MyExtStruct"), None);
442+
insta::assert_debug_snapshot!(initializer.constructors.get("MyExtStruct"), @r#""#);
443+
}
190444
}

compiler/plc_lowering/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use plc_ast::{ast::AstNode, provider::IdProvider};
66

77
pub mod inheritance;
88
pub mod initializer;
9+
pub mod lower_ast;
910
#[cfg(test)]
1011
mod tests;
1112

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//! A low level representation of the AST
2+
//! This module defines a lower-level representation of the Abstract Syntax Tree (AST)
3+
//! it defines only simple data structures without any complex logic
4+
5+
pub struct LoweredAst {
6+
nodes: Vec<LoweredNode>,
7+
}
8+
9+
pub enum LoweredNode {
10+
TypeDef(TypeDef),
11+
TypeNode(TypeNode),
12+
Variable,
13+
Declaration,
14+
Assignment,
15+
Call,
16+
Return,
17+
}
18+
19+
pub struct TypeDef {
20+
name: String,
21+
target: String,
22+
}
23+
24+
pub struct TypeNode {
25+
name: String,
26+
size: usize,
27+
elements: Vec<TypeNode>,
28+
}
29+
30+
pub struct Variable {
31+
name: String,
32+
var_type: String,
33+
value: Vec<String>,
34+
}

0 commit comments

Comments
 (0)