1
- use std:: fmt:: Display ;
1
+ use std:: { fmt:: Display , vec} ;
2
+
3
+ use plc_ast:: ast:: { AstNode , AstStatement , ConfigVariable , ReferenceAccess , ReferenceExpr } ;
4
+ use plc_diagnostics:: diagnostics:: Diagnostic ;
2
5
3
6
use crate :: { index:: Index , typesystem:: Dimension } ;
4
7
5
- #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
8
+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
6
9
pub enum ExpressionPathElement < ' idx > {
7
10
Name ( & ' idx str ) ,
8
- ArrayAccess ( & ' idx [ Dimension ] ) ,
11
+ ArrayDimensions ( & ' idx [ Dimension ] ) ,
12
+ ArrayAccess ( Vec < i128 > ) ,
9
13
}
10
14
11
15
impl Display for ExpressionPathElement < ' _ > {
12
16
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
13
17
match self {
14
18
ExpressionPathElement :: Name ( name) => write ! ( f, "{name}" ) ,
19
+ ExpressionPathElement :: ArrayDimensions ( _) => unimplemented ! ( ) ,
15
20
ExpressionPathElement :: ArrayAccess ( _) => unimplemented ! ( ) ,
16
21
}
17
22
}
@@ -25,7 +30,7 @@ impl<'idx> From<&'idx str> for ExpressionPathElement<'idx> {
25
30
26
31
impl < ' idx > From < & ' idx [ Dimension ] > for ExpressionPathElement < ' idx > {
27
32
fn from ( dims : & ' idx [ Dimension ] ) -> Self {
28
- ExpressionPathElement :: ArrayAccess ( dims)
33
+ ExpressionPathElement :: ArrayDimensions ( dims)
29
34
}
30
35
}
31
36
@@ -51,8 +56,8 @@ impl<'idx> ExpressionPath<'idx> {
51
56
let mut levels: Vec < Vec < String > > = vec ! [ ] ;
52
57
for seg in self . names . iter ( ) {
53
58
let level = match seg {
54
- crate :: expression_path :: ExpressionPathElement :: Name ( s) => vec ! [ s. to_string( ) ] ,
55
- crate :: expression_path :: ExpressionPathElement :: ArrayAccess ( dimensions) => {
59
+ ExpressionPathElement :: Name ( s) => vec ! [ s. to_string( ) ] ,
60
+ ExpressionPathElement :: ArrayDimensions ( dimensions) => {
56
61
let mut array = dimensions. iter ( ) . map ( |it| it. get_range_inclusive ( index) . unwrap ( ) ) . fold (
57
62
vec ! [ ] ,
58
63
|curr, it| {
@@ -74,6 +79,14 @@ impl<'idx> ExpressionPath<'idx> {
74
79
array. iter_mut ( ) . for_each ( |s| * s = format ! ( "[{s}]" ) ) ;
75
80
array
76
81
}
82
+ ExpressionPathElement :: ArrayAccess ( idx) => {
83
+ let Some ( first) = idx. first ( ) else { unreachable ! ( "Caught at the parsing stage" ) } ;
84
+
85
+ let access =
86
+ idx. iter ( ) . skip ( 1 ) . fold ( format ! ( "{first}" ) , |acc, idx| format ! ( "{acc},{idx}" ) ) ;
87
+
88
+ vec ! [ format!( "[{access}]" ) ]
89
+ }
77
90
} ;
78
91
levels. push ( level) ;
79
92
}
@@ -94,6 +107,76 @@ impl<'idx> ExpressionPath<'idx> {
94
107
}
95
108
}
96
109
110
+ impl < ' a > TryFrom < & ' a ConfigVariable > for ExpressionPath < ' a > {
111
+ type Error = Vec < Diagnostic > ;
112
+
113
+ fn try_from ( value : & ' a ConfigVariable ) -> Result < Self , Self :: Error > {
114
+ let mut names = get_expression_path_segments ( & value. reference ) ?;
115
+ names. reverse ( ) ;
116
+ Ok ( Self { names } )
117
+ }
118
+ }
119
+
120
+ // Transforms a `ConfigVariable`'s 'AstNode' into a collection of corresponding `ExpressionPathElement`s.
121
+ // This function will traverse the AST top-to-bottom, collecting segments along the way, which means the order of the collection
122
+ // needs to be reversed by the caller to match the written expression.
123
+ fn get_expression_path_segments ( node : & AstNode ) -> Result < Vec < ExpressionPathElement > , Vec < Diagnostic > > {
124
+ let mut paths = vec ! [ ] ;
125
+ let mut diagnostics = vec ! [ ] ;
126
+ let mut add_diagnostic = |location| {
127
+ diagnostics. push (
128
+ Diagnostic :: new ( "VAR_CONFIG array access must be a literal integer" ) . with_location ( location) ,
129
+ ) ;
130
+ } ;
131
+ match & node. stmt {
132
+ AstStatement :: ReferenceExpr ( ReferenceExpr { access : ReferenceAccess :: Member ( reference) , base } ) => {
133
+ paths. push ( ExpressionPathElement :: Name ( reference. get_flat_reference_name ( ) . unwrap_or_default ( ) ) ) ;
134
+ if let Some ( base) = base {
135
+ match get_expression_path_segments ( base) {
136
+ Ok ( v) => paths. extend ( v) ,
137
+ Err ( e) => diagnostics. extend ( e) ,
138
+ } ;
139
+ }
140
+ }
141
+ AstStatement :: ReferenceExpr ( ReferenceExpr { access : ReferenceAccess :: Index ( idx) , base } ) => {
142
+ match & idx. as_ref ( ) . stmt {
143
+ AstStatement :: Literal ( _) => {
144
+ if let Some ( v) = idx. get_literal_integer_value ( ) . map ( |it| vec ! [ it] ) {
145
+ paths. push ( ExpressionPathElement :: ArrayAccess ( v) )
146
+ } else {
147
+ add_diagnostic ( & idx. location ) ;
148
+ }
149
+ }
150
+ AstStatement :: ExpressionList ( vec) => {
151
+ let mut res = vec ! [ ] ;
152
+ vec. iter ( ) . for_each ( |idx : & AstNode | {
153
+ if let Some ( v) = idx. get_literal_integer_value ( ) {
154
+ res. push ( v) ;
155
+ } else {
156
+ add_diagnostic ( & idx. location ) ;
157
+ }
158
+ } ) ;
159
+ paths. push ( ExpressionPathElement :: ArrayAccess ( res) ) ;
160
+ }
161
+ _ => add_diagnostic ( & idx. location ) ,
162
+ }
163
+ if let Some ( base) = base {
164
+ match get_expression_path_segments ( base) {
165
+ Ok ( v) => paths. extend ( v) ,
166
+ Err ( e) => diagnostics. extend ( e) ,
167
+ } ;
168
+ }
169
+ }
170
+ _ => add_diagnostic ( & node. location ) ,
171
+ } ;
172
+
173
+ if !diagnostics. is_empty ( ) {
174
+ return Err ( diagnostics) ;
175
+ }
176
+
177
+ Ok ( paths)
178
+ }
179
+
97
180
impl < ' a > From < Vec < ExpressionPathElement < ' a > > > for ExpressionPath < ' a > {
98
181
fn from ( v : Vec < ExpressionPathElement < ' a > > ) -> Self {
99
182
ExpressionPath { names : v }
@@ -132,7 +215,7 @@ mod tests {
132
215
} ] ;
133
216
134
217
let name = ExpressionPath {
135
- names : vec ! [ ExpressionPathElement :: Name ( "a" ) , ExpressionPathElement :: ArrayAccess ( & dims) ] ,
218
+ names : vec ! [ ExpressionPathElement :: Name ( "a" ) , ExpressionPathElement :: ArrayDimensions ( & dims) ] ,
136
219
} ;
137
220
let index = Index :: default ( ) ;
138
221
assert_eq ! ( name. expand( & index) , vec![ "a[-1]" . to_string( ) , "a[0]" . to_string( ) , "a[1]" . to_string( ) , ] )
@@ -146,7 +229,7 @@ mod tests {
146
229
} ] ;
147
230
148
231
let name = ExpressionPath {
149
- names : vec ! [ ExpressionPathElement :: Name ( "a" ) , ExpressionPathElement :: ArrayAccess ( & dims) ] ,
232
+ names : vec ! [ ExpressionPathElement :: Name ( "a" ) , ExpressionPathElement :: ArrayDimensions ( & dims) ] ,
150
233
} ;
151
234
let index = Index :: default ( ) ;
152
235
assert_eq ! ( name. expand( & index) , vec![ "a[1]" . to_string( ) , ] )
@@ -161,7 +244,7 @@ mod tests {
161
244
] ;
162
245
163
246
let name = ExpressionPath {
164
- names : vec ! [ ExpressionPathElement :: Name ( "a" ) , ExpressionPathElement :: ArrayAccess ( & dims) ] ,
247
+ names : vec ! [ ExpressionPathElement :: Name ( "a" ) , ExpressionPathElement :: ArrayDimensions ( & dims) ] ,
165
248
} ;
166
249
let index = Index :: default ( ) ;
167
250
let mut res = name. expand ( & index) ;
@@ -197,9 +280,9 @@ mod tests {
197
280
let name = ExpressionPath {
198
281
names : vec ! [
199
282
ExpressionPathElement :: Name ( "a" ) ,
200
- ExpressionPathElement :: ArrayAccess ( & dims1) ,
201
- ExpressionPathElement :: ArrayAccess ( & dims2) ,
202
- ExpressionPathElement :: ArrayAccess ( & dims3) ,
283
+ ExpressionPathElement :: ArrayDimensions ( & dims1) ,
284
+ ExpressionPathElement :: ArrayDimensions ( & dims2) ,
285
+ ExpressionPathElement :: ArrayDimensions ( & dims3) ,
203
286
] ,
204
287
} ;
205
288
let index = Index :: default ( ) ;
0 commit comments