Skip to content

Commit ca9a577

Browse files
authored
fix(parser): Array access on fn-call result (#1431)
This commit allows for code such as `foo()[3];` where `foo` is some POU returning an array
1 parent 9b10ef7 commit ca9a577

6 files changed

+270
-18
lines changed

src/codegen/tests/function_tests.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,3 +403,24 @@ fn argument_fed_by_ref_then_by_val() {
403403

404404
insta::assert_snapshot!(result)
405405
}
406+
407+
#[test]
408+
fn function_call_with_array_access() {
409+
let result = codegen(
410+
"
411+
FUNCTION foo : ARRAY[1..5] OF DINT
412+
foo := [5, 4, 3, 2, 1];
413+
END_FUNCTION
414+
415+
FUNCTION main
416+
VAR
417+
value : DINT;
418+
END_VAR
419+
420+
value := foo()[3];
421+
END_FUNCTION
422+
",
423+
);
424+
425+
insta::assert_snapshot!(result)
426+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
source: src/codegen/tests/function_tests.rs
3+
expression: result
4+
---
5+
; ModuleID = '<internal>'
6+
source_filename = "<internal>"
7+
8+
define void @foo(i32* %0) {
9+
entry:
10+
%foo = alloca i32*, align 8
11+
store i32* %0, i32** %foo, align 8
12+
%deref = load i32*, i32** %foo, align 8
13+
store [5 x i32] [i32 5, i32 4, i32 3, i32 2, i32 1], i32* %deref, align 4
14+
ret void
15+
}
16+
17+
define void @main() {
18+
entry:
19+
%value = alloca i32, align 4
20+
store i32 0, i32* %value, align 4
21+
%__foo0 = alloca [5 x i32], align 4
22+
%0 = bitcast [5 x i32]* %__foo0 to i8*
23+
call void @llvm.memset.p0i8.i64(i8* align 1 %0, i8 0, i64 ptrtoint ([5 x i32]* getelementptr ([5 x i32], [5 x i32]* null, i32 1) to i64), i1 false)
24+
%1 = bitcast [5 x i32]* %__foo0 to i32*
25+
call void @foo(i32* %1)
26+
%tmpVar = getelementptr inbounds [5 x i32], [5 x i32]* %__foo0, i32 0, i32 2
27+
%load_tmpVar = load i32, i32* %tmpVar, align 4
28+
store i32 %load_tmpVar, i32* %value, align 4
29+
ret void
30+
}
31+
32+
; Function Attrs: argmemonly nofree nounwind willreturn writeonly
33+
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #0
34+
35+
attributes #0 = { argmemonly nofree nounwind willreturn writeonly }
36+
; ModuleID = '__init___testproject'
37+
source_filename = "__init___testproject"
38+
39+
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]
40+
41+
define void @__init___testproject() {
42+
entry:
43+
ret void
44+
}

src/parser/expressions_parser.rs

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -359,27 +359,45 @@ fn parse_null_literal(lexer: &mut ParseSession) -> Option<AstNode> {
359359

360360
pub fn parse_call_statement(lexer: &mut ParseSession) -> Option<AstNode> {
361361
let reference = parse_qualified_reference(lexer)?;
362+
let reference_loc = reference.get_location();
362363

363-
// is this a callstatement?
364-
if lexer.try_consume(KeywordParensOpen) {
365-
let start = reference.get_location();
366-
// Call Statement
367-
let call_statement = if lexer.try_consume(KeywordParensClose) {
368-
AstFactory::create_call_statement(reference, None, lexer.next_id(), start.span(&lexer.location()))
369-
} else {
370-
parse_any_in_region(lexer, vec![KeywordParensClose], |lexer| {
371-
AstFactory::create_call_statement(
372-
reference,
373-
Some(parse_expression_list(lexer)),
374-
lexer.next_id(),
375-
start.span(&lexer.location()),
376-
)
377-
})
378-
};
379-
Some(call_statement)
364+
// We're not dealing with a call statement here
365+
if !lexer.try_consume(KeywordParensOpen) {
366+
return Some(reference);
367+
}
368+
369+
let call = if lexer.try_consume(KeywordParensClose) {
370+
AstFactory::create_call_statement(
371+
reference,
372+
None,
373+
lexer.next_id(),
374+
reference_loc.span(&lexer.location()),
375+
)
380376
} else {
381-
Some(reference)
377+
parse_any_in_region(lexer, vec![KeywordParensClose], |lexer| {
378+
AstFactory::create_call_statement(
379+
reference,
380+
Some(parse_expression_list(lexer)),
381+
lexer.next_id(),
382+
reference_loc.span(&lexer.location()),
383+
)
384+
})
385+
};
386+
387+
// Are we dealing with an array-index access directly after the call, e.g. `foo()[...]`?
388+
if lexer.try_consume(KeywordSquareParensOpen) {
389+
let index = parse_any_in_region(lexer, vec![KeywordSquareParensClose], parse_expression);
390+
let statement = AstFactory::create_index_reference(
391+
index,
392+
Some(call),
393+
lexer.next_id(),
394+
SourceLocation::undefined(),
395+
);
396+
397+
return Some(statement);
382398
}
399+
400+
Some(call)
383401
}
384402

385403
pub fn parse_qualified_reference(lexer: &mut ParseSession) -> Option<AstNode> {

src/parser/tests/expressions_parser_tests.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,3 +1740,19 @@ fn parenthesized_expression_span() {
17401740
let range = array.elements().unwrap().get_location().get_span().to_range().unwrap();
17411741
assert_eq!(&src[range.start..range.end], "(1 + 2)");
17421742
}
1743+
1744+
#[test]
1745+
fn function_call_array_index() {
1746+
let src = "
1747+
PROGRAM prg
1748+
foo()[1];
1749+
foo()[1 + 2]
1750+
foo()[one];
1751+
foo()[one + two];
1752+
foo()[bar()];
1753+
END_PROGRAM
1754+
";
1755+
1756+
let parse_result = parse(src).0;
1757+
assert_debug_snapshot!(parse_result.implementations[0].statements);
1758+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
source: src/parser/tests/expressions_parser_tests.rs
3+
expression: "parse_result.implementations[0].statements"
4+
---
5+
[
6+
ReferenceExpr {
7+
kind: Index(
8+
LiteralInteger {
9+
value: 1,
10+
},
11+
),
12+
base: Some(
13+
CallStatement {
14+
operator: ReferenceExpr {
15+
kind: Member(
16+
Identifier {
17+
name: "foo",
18+
},
19+
),
20+
base: None,
21+
},
22+
parameters: None,
23+
},
24+
),
25+
},
26+
ReferenceExpr {
27+
kind: Index(
28+
BinaryExpression {
29+
operator: Plus,
30+
left: LiteralInteger {
31+
value: 1,
32+
},
33+
right: LiteralInteger {
34+
value: 2,
35+
},
36+
},
37+
),
38+
base: Some(
39+
CallStatement {
40+
operator: ReferenceExpr {
41+
kind: Member(
42+
Identifier {
43+
name: "foo",
44+
},
45+
),
46+
base: None,
47+
},
48+
parameters: None,
49+
},
50+
),
51+
},
52+
ReferenceExpr {
53+
kind: Index(
54+
BinaryExpression {
55+
operator: Plus,
56+
left: ReferenceExpr {
57+
kind: Member(
58+
Identifier {
59+
name: "one",
60+
},
61+
),
62+
base: None,
63+
},
64+
right: ReferenceExpr {
65+
kind: Member(
66+
Identifier {
67+
name: "two",
68+
},
69+
),
70+
base: None,
71+
},
72+
},
73+
),
74+
base: Some(
75+
CallStatement {
76+
operator: ReferenceExpr {
77+
kind: Member(
78+
Identifier {
79+
name: "foo",
80+
},
81+
),
82+
base: None,
83+
},
84+
parameters: None,
85+
},
86+
),
87+
},
88+
ReferenceExpr {
89+
kind: Index(
90+
CallStatement {
91+
operator: ReferenceExpr {
92+
kind: Member(
93+
Identifier {
94+
name: "bar",
95+
},
96+
),
97+
base: None,
98+
},
99+
parameters: None,
100+
},
101+
),
102+
base: Some(
103+
CallStatement {
104+
operator: ReferenceExpr {
105+
kind: Member(
106+
Identifier {
107+
name: "foo",
108+
},
109+
),
110+
base: None,
111+
},
112+
parameters: None,
113+
},
114+
),
115+
},
116+
]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: (%COMPILE %s && %RUN) | %CHECK %s
2+
3+
FUNCTION foo: ARRAY[1..5] OF DINT
4+
foo := [5, 4, 3, 2, 1];
5+
END_FUNCTION
6+
7+
FUNCTION alwaysOne: DINT
8+
alwaysOne := 1;
9+
END_FUNCTION
10+
11+
FUNCTION main
12+
VAR
13+
value: DINT;
14+
one: DINT := 1;
15+
two: DINT := 2;
16+
END_VAR
17+
18+
// CHECK: 5
19+
value := foo()[1];
20+
printf('%d$N', value);
21+
22+
// CHECK: 3
23+
value := foo()[1 + 2];
24+
printf('%d$N', value);
25+
26+
// CHECK: 5
27+
value := foo()[one];
28+
printf('%d$N', value);
29+
30+
// CHECK: 3
31+
value := foo()[one + two];
32+
printf('%d$N', value);
33+
34+
// CHECK: 5
35+
value := foo()[alwaysOne()];
36+
printf('%d$N', value);
37+
END_FUNCTION

0 commit comments

Comments
 (0)