ironplc_dsl/
dsl.rs

1use core::str::FromStr;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4use time::Duration;
5
6use crate::ast::*;
7use crate::core::{Id, SourceLoc};
8use crate::sfc::Network;
9
10pub enum TypeDefinitionKind {
11    Enumeration,
12    FunctionBlock,
13    Function,
14    Structure,
15}
16
17/// Defines the top-level elements that are valid declarations in a library.
18#[derive(Debug, PartialEq)]
19pub enum LibraryElement {
20    DataTypeDeclaration(Vec<EnumerationDeclaration>),
21    FunctionDeclaration(FunctionDeclaration),
22    // TODO
23    FunctionBlockDeclaration(FunctionBlockDeclaration),
24    ProgramDeclaration(ProgramDeclaration),
25    ConfigurationDeclaration(ConfigurationDeclaration),
26}
27
28/// IEC 61131-3 integer.
29///
30/// Underlying data type is a String to trace back to the original
31/// representation if the value is not valid.
32pub struct Integer {
33    value: String,
34}
35
36impl Integer {
37    pub fn try_from<T: FromStr>(&self) -> T {
38        let v: String = self.value.chars().filter(|c| c.is_digit(10)).collect();
39        match v.parse::<T>() {
40            Ok(v) => return v,
41            Err(_) => panic!("out of range"),
42        }
43    }
44
45    pub fn as_type<T: FromStr>(&self) -> T {
46        self.try_from::<T>()
47    }
48
49    pub fn num_chars(&self) -> u8 {
50        let value: String = self.value.chars().filter(|c| c.is_digit(10)).collect();
51        // TODO This is most obviously wrong
52        let value: u8 = 1;
53        value
54    }
55
56    pub fn from(a: &str) -> Integer {
57        Integer {
58            value: String::from(a),
59        }
60    }
61}
62
63pub struct SignedInteger {
64    value: Integer,
65    is_neg: bool,
66}
67
68impl SignedInteger {
69    pub fn as_type<T: FromStr>(&self) -> T {
70        let val = self.value.try_from::<T>();
71        // TODO
72        //if self.is_neg {
73        //    val *= -1;
74        //}
75        val
76    }
77    pub fn from(a: &str) -> SignedInteger {
78        match a.chars().nth(0) {
79            Some('+') => {
80                return SignedInteger {
81                    value: Integer::from(a.get(1..).unwrap()),
82                    is_neg: false,
83                }
84            }
85            Some('-') => {
86                return SignedInteger {
87                    value: Integer::from(a.get(1..).unwrap()),
88                    is_neg: true,
89                }
90            }
91            _ => {
92                return SignedInteger {
93                    value: Integer::from(a),
94                    is_neg: false,
95                }
96            }
97        }
98    }
99}
100
101#[derive(Debug, PartialEq, Clone)]
102pub struct Float {
103    pub value: f64,
104    pub data_type: Option<Id>,
105}
106
107// TODO I don't know if I need to support multiple storage classes for the
108// same value.
109// 2.4.3 Declaration
110#[derive(Debug, PartialEq, Clone)]
111pub struct Declaration {
112    pub name: Id,
113    pub storage_class: StorageClass,
114    pub at: Option<At>,
115    pub initializer: Option<TypeInitializer>,
116}
117
118#[derive(Debug, PartialEq, Clone)]
119pub enum VariableType {
120    /// Local to a POU.
121    Var,
122    /// Local to a POU. Does not need to be maintained
123    /// between calls to a POU.
124    VarTemp,
125    /// Variable that is visible to a calling POU as an input.
126    Input,
127    /// Variable that is visible to calling POU and can only
128    /// be ready from the calling POU. It can be written to
129    /// by the POU that defines the variable.
130    Output,
131    /// Variable that is visible to calling POU and is readable
132    /// writeable by the calling POU.
133    InOut,
134    /// Enables a POU to read and (possibly) write to a global
135    /// variable.
136    External,
137    /// A variable that may be read and written by multiple
138    /// POUs that also declare the variable as external.
139    Global,
140    /// Configurations for communication channels.
141    Access,
142}
143
144#[derive(Debug, PartialEq, Clone)]
145pub struct VarInitDecl {
146    pub name: Id,
147    pub var_type: VariableType,
148    pub storage_class: StorageClass,
149    pub initializer: TypeInitializer,
150    // TODO this need much more
151    pub position: SourceLoc,
152}
153
154impl VarInitDecl {
155    /// Creates a variable declaration for simple type and no initialization.
156    pub fn simple_input(name: &str, type_name: &str, loc: SourceLoc) -> VarInitDecl {
157        VarInitDecl::simple(name, type_name, VariableType::Input, loc)
158    }
159
160    /// Creates a variable declaration for simple type and no initialization.
161    pub fn simple_output(name: &str, type_name: &str, loc: SourceLoc) -> VarInitDecl {
162        VarInitDecl::simple(name, type_name, VariableType::Output, loc)
163    }
164
165    /// Creates a variable declaration for simple type and no initialization.
166    pub fn simple_var(name: &str, type_name: &str, loc: SourceLoc) -> VarInitDecl {
167        VarInitDecl::simple(name, type_name, VariableType::Var, loc)
168    }
169
170    /// Creates a variable declaration for simple type and no initialization.
171    pub fn simple_external(name: &str, type_name: &str, loc: SourceLoc) -> VarInitDecl {
172        VarInitDecl::simple(name, type_name, VariableType::External, loc)
173    }
174
175    /// Creates a variable declaration for simple type and no initialization.
176    pub fn simple(
177        name: &str,
178        type_name: &str,
179        var_type: VariableType,
180        loc: SourceLoc,
181    ) -> VarInitDecl {
182        VarInitDecl {
183            name: Id::from(name),
184            var_type: var_type,
185            storage_class: StorageClass::Unspecified,
186            initializer: TypeInitializer::Simple {
187                type_name: Id::from(type_name),
188                initial_value: None,
189            },
190            position: loc,
191        }
192    }
193
194    /// Creates a variable declaration for enumeration having an initial value.
195    pub fn enumerated_input(
196        name: &str,
197        type_name: &str,
198        initial_value: &str,
199        loc: SourceLoc,
200    ) -> VarInitDecl {
201        VarInitDecl {
202            name: Id::from(name),
203            var_type: VariableType::Input,
204            storage_class: StorageClass::Unspecified,
205            initializer: TypeInitializer::EnumeratedType(EnumeratedTypeInitializer {
206                type_name: Id::from(type_name),
207                initial_value: Some(Id::from(initial_value)),
208            }),
209            position: loc,
210        }
211    }
212
213    /// Creates a variable declaration for a function block.
214    pub fn function_block_var(name: &str, type_name: &str, loc: SourceLoc) -> VarInitDecl {
215        VarInitDecl {
216            name: Id::from(name),
217            var_type: VariableType::Var,
218            storage_class: StorageClass::Unspecified,
219            initializer: TypeInitializer::FunctionBlock(FunctionBlockTypeInitializer {
220                type_name: Id::from(type_name),
221            }),
222            position: loc,
223        }
224    }
225
226    /// Creates a variable declaration that is ambiguous on the type.
227    ///
228    /// The language has some ambiguity for types. The late bound represents
229    /// a placeholder that is later resolved once all types are known.
230    pub fn late_bound_input(name: &str, type_name: &str, loc: SourceLoc) -> VarInitDecl {
231        VarInitDecl::late_bound(name, type_name, VariableType::Input, loc)
232    }
233
234    /// Creates a variable declaration that is ambiguous on the type.
235    ///
236    /// The language has some ambiguity for types. The late bound represents
237    /// a placeholder that is later resolved once all types are known.
238    pub fn late_bound_var(name: &str, type_name: &str, loc: SourceLoc) -> VarInitDecl {
239        VarInitDecl::late_bound(name, type_name, VariableType::Var, loc)
240    }
241
242    /// Creates a variable declaration that is ambiguous on the type.
243    ///
244    /// The language has some ambiguity for types. The late bound represents
245    /// a placeholder that is later resolved once all types are known.
246    pub fn late_bound(
247        name: &str,
248        type_name: &str,
249        var_type: VariableType,
250        loc: SourceLoc,
251    ) -> VarInitDecl {
252        VarInitDecl {
253            name: Id::from(name),
254            var_type: var_type,
255            storage_class: StorageClass::Unspecified,
256            initializer: TypeInitializer::LateResolvedType(Id::from(type_name)),
257            position: loc,
258        }
259    }
260}
261
262#[derive(Debug, PartialEq, Clone)]
263pub struct LocatedVarInit {
264    pub name: Option<Id>,
265    pub storage_class: StorageClass,
266    pub at: DirectVariable,
267    pub initializer: TypeInitializer,
268}
269
270#[derive(Debug, PartialEq, Clone)]
271pub enum VarInitKind {
272    VarInit(VarInitDecl),
273    LocatedVarInit(LocatedVarInit),
274}
275
276// 2.4.3.1 Type assignment
277#[derive(Debug, PartialEq, Clone)]
278pub struct At {}
279
280// 2.7.2 Tasks
281#[derive(Debug, PartialEq)]
282pub struct TaskConfiguration {
283    pub name: Id,
284    pub priority: u32,
285    // TODO this might not be optional
286    pub interval: Option<Duration>,
287}
288
289#[derive(PartialEq, Clone, Debug)]
290pub enum Constant {
291    // TODO these need values
292    IntegerLiteral(i128),
293    RealLiteral(Float),
294    CharacterString(),
295    Duration(Duration),
296    TimeOfDay(),
297    Date(),
298    DateAndTime(),
299}
300
301#[derive(PartialEq, Clone)]
302pub enum Initializer {
303    Simple(Constant),
304    Subrange(),
305    Enumerated(),
306    Array(),
307    InitializedStructure(),
308    SingleByteString(),
309    DoubleByteString(),
310}
311
312impl fmt::Debug for Initializer {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        f.debug_struct("Initializer").finish()
315    }
316}
317
318#[derive(PartialEq, Clone, Debug)]
319pub struct EnumeratedTypeInitializer {
320    pub type_name: Id,
321    pub initial_value: Option<Id>,
322}
323
324#[derive(PartialEq, Clone, Debug)]
325pub struct EnumeratedValuesInitializer {
326    pub values: Vec<Id>,
327    pub initial_value: Option<Id>,
328    pub position: SourceLoc,
329}
330
331#[derive(PartialEq, Clone, Debug)]
332pub struct FunctionBlockTypeInitializer {
333    pub type_name: Id,
334}
335
336#[derive(PartialEq, Clone, Debug)]
337pub enum TypeInitializer {
338    Simple {
339        type_name: Id,
340        initial_value: Option<Initializer>,
341    },
342    EnumeratedValues(EnumeratedValuesInitializer),
343    EnumeratedType(EnumeratedTypeInitializer),
344    FunctionBlock(FunctionBlockTypeInitializer),
345    Structure {
346        // TODO
347        type_name: Id,
348    },
349    /// Type that is ambiguous until have discovered type
350    /// definitions. Value is the name of the type.
351    LateResolvedType(Id),
352}
353
354impl TypeInitializer {
355    pub fn simple_uninitialized(type_name: &str) -> TypeInitializer {
356        TypeInitializer::Simple {
357            type_name: Id::from(type_name),
358            initial_value: None,
359        }
360    }
361
362    pub fn simple(type_name: &str, value: Initializer) -> TypeInitializer {
363        TypeInitializer::Simple {
364            type_name: Id::from(type_name),
365            initial_value: Some(value),
366        }
367    }
368
369    pub fn enumerated_values(
370        values: Vec<Id>,
371        initial_value: Option<Id>,
372        position: SourceLoc,
373    ) -> TypeInitializer {
374        TypeInitializer::EnumeratedValues(EnumeratedValuesInitializer {
375            values: values,
376            initial_value: initial_value,
377            position: position,
378        })
379    }
380}
381
382#[derive(Debug, PartialEq, Clone)]
383pub enum LocationPrefix {
384    I,
385    Q,
386    M,
387}
388
389impl LocationPrefix {
390    pub fn from_char(l: char) -> LocationPrefix {
391        match l {
392            'I' => return LocationPrefix::I,
393            'Q' => return LocationPrefix::Q,
394            'M' => return LocationPrefix::M,
395            // TODO error message
396            _ => panic!(),
397        }
398    }
399}
400
401#[derive(Debug, PartialEq, Clone)]
402pub enum SizePrefix {
403    Nil,
404    X,
405    B,
406    W,
407    D,
408    L,
409}
410
411impl SizePrefix {
412    pub fn from_char(s: char) -> SizePrefix {
413        match s {
414            'X' => return SizePrefix::X,
415            'B' => return SizePrefix::B,
416            'W' => return SizePrefix::W,
417            'D' => return SizePrefix::D,
418            'L' => return SizePrefix::L,
419            // TODO error message
420            _ => panic!(),
421        }
422    }
423}
424
425#[derive(Debug, PartialEq, Clone)]
426pub enum StorageClass {
427    // TODO Some of these are not valid for some contexts - should there be multiple
428    // storage classes, indicate some how, or fail?
429    Unspecified,
430    Constant,
431    /// Stored so that the value is retained through power loss.
432    Retain,
433    /// Stored so that the value is NOT retained through power loss.
434    NonRetain,
435}
436
437/// Resource assigns tasks to a particular CPU.
438#[derive(Debug, PartialEq)]
439pub struct ResourceDeclaration {
440    /// Symbolic name for a CPU
441    pub name: Id,
442    /// The identifier for a CPU
443    pub resource: Id,
444    /// Global variables in the scope of the resource.
445    ///
446    /// Global variables are not in scope for other resources.
447    pub global_vars: Vec<Declaration>,
448    /// Defines the configuration of programs on this resource.
449    pub tasks: Vec<TaskConfiguration>,
450    /// Defines runnable programs.
451    ///
452    /// A runnable program can be associated with a task configuration
453    /// by name.
454    pub programs: Vec<ProgramConfiguration>,
455}
456
457#[derive(Debug, PartialEq)]
458pub struct ProgramConfiguration {
459    pub name: Id,
460    pub task_name: Option<Id>,
461    pub type_name: Id,
462}
463
464#[derive(Debug, PartialEq)]
465pub struct ConfigurationDeclaration {
466    pub name: Id,
467    pub global_var: Vec<Declaration>,
468    pub resource_decl: Vec<ResourceDeclaration>,
469}
470
471#[derive(Debug, PartialEq)]
472pub struct EnumerationDeclaration {
473    pub name: Id,
474    // TODO need to understand when the context name matters in the definition
475    pub spec: EnumeratedSpecificationKind,
476    pub default: Option<Id>,
477}
478
479#[derive(Debug, PartialEq)]
480pub struct EnumeratedSpecificationValues {
481    pub ids: Vec<Id>,
482    pub position: SourceLoc,
483}
484
485#[derive(Debug, PartialEq)]
486pub enum EnumeratedSpecificationKind {
487    TypeName(Id),
488    /// Enumeration declaration that provides a list of values.
489    ///
490    /// Order of the values is important because the order declares the
491    /// default value if no default is specified directly.
492    Values(EnumeratedSpecificationValues),
493}
494
495impl EnumeratedSpecificationKind {
496    pub fn values(values: Vec<Id>, position: SourceLoc) -> EnumeratedSpecificationKind {
497        EnumeratedSpecificationKind::Values(EnumeratedSpecificationValues {
498            ids: values,
499            position: position,
500        })
501    }
502}
503
504#[derive(PartialEq, Clone)]
505pub struct DirectVariable {
506    pub location: LocationPrefix,
507    pub size: SizePrefix,
508    pub address: Vec<u32>,
509}
510
511impl fmt::Debug for DirectVariable {
512    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513        f.debug_struct("DirectVariable")
514            .field("location", &self.location)
515            .field("size", &self.size)
516            .finish()
517    }
518}
519
520impl fmt::Display for DirectVariable {
521    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522        f.debug_struct("DirectVariable")
523            .field("location", &self.location)
524            .field("size", &self.size)
525            .finish()
526    }
527}
528
529#[derive(Debug, PartialEq, Clone)]
530pub struct Sfc {
531    pub networks: Vec<Network>,
532}
533
534#[derive(Debug, PartialEq, Clone)]
535pub struct Statements {
536    pub body: Vec<StmtKind>,
537}
538
539#[derive(Debug, PartialEq, Clone)]
540pub enum FunctionBlockBody {
541    Sfc(Sfc),
542    Statements(Statements),
543    /// A function block that has no body (and is therefore no known type).
544    ///
545    /// This type is not strictly valid, but highly useful and can be detected
546    /// with a semantic rule.
547    Empty(),
548}
549
550impl FunctionBlockBody {
551    pub fn stmts(stmts: Vec<StmtKind>) -> FunctionBlockBody {
552        FunctionBlockBody::Statements(Statements { body: stmts })
553    }
554
555    pub fn sfc(networks: Vec<Network>) -> FunctionBlockBody {
556        FunctionBlockBody::Sfc(Sfc { networks: networks })
557    }
558
559    pub fn empty() -> FunctionBlockBody {
560        FunctionBlockBody::Empty()
561    }
562}
563
564#[derive(Debug, PartialEq, Clone)]
565pub struct FunctionDeclaration {
566    pub name: Id,
567    pub return_type: Id,
568    // TODO rename these to be descriptive
569    pub inputs: Vec<VarInitDecl>,
570    pub outputs: Vec<VarInitDecl>,
571    pub inouts: Vec<VarInitDecl>,
572    pub vars: Vec<VarInitDecl>,
573    pub externals: Vec<VarInitDecl>,
574    // TODO other types
575    pub body: Vec<StmtKind>,
576}
577
578#[derive(Debug, PartialEq, Clone)]
579pub struct FunctionBlockDeclaration {
580    pub name: Id,
581    pub inputs: Vec<VarInitDecl>,
582    pub outputs: Vec<VarInitDecl>,
583    pub inouts: Vec<VarInitDecl>,
584    pub vars: Vec<VarInitDecl>,
585    pub externals: Vec<VarInitDecl>,
586    // TODO other var declarations
587    pub body: FunctionBlockBody,
588}
589
590#[derive(Debug, PartialEq)]
591pub struct ProgramDeclaration {
592    pub type_name: Id,
593    pub inputs: Vec<VarInitDecl>,
594    pub outputs: Vec<VarInitDecl>,
595    pub inouts: Vec<VarInitDecl>,
596    pub vars: Vec<VarInitDecl>,
597    // TODO other var declarations
598    // TODO located var declarations
599    // TODO other stuff here
600    pub body: FunctionBlockBody,
601}
602
603#[derive(Debug, PartialEq)]
604pub struct Library {
605    pub elems: Vec<LibraryElement>,
606}
607
608impl Library {
609    pub fn new(elems: Vec<LibraryElement>) -> Self {
610        Library { elems: elems }
611    }
612}