Skip to main content

plc_lexer/lexer/
tokens.rs

1use logos::Logos;
2
3use plc_ast::ast::{DirectAccessType, HardwareAccessType};
4
5#[derive(Debug, PartialEq, Eq, Logos, Clone, Copy)]
6pub enum Token {
7    #[error]
8    #[regex(r"\(\*", |lex| super::parse_comments(lex))]
9    #[regex(r"/\*", |lex| super::parse_comments(lex))]
10    #[regex(r"\{", |lex| super::parse_pragma(lex))]
11    #[regex(r"//.*", logos::skip)]
12    #[regex(r"(?m)\r", logos::skip)]
13    Error,
14
15    #[token("@EXTERNAL")]
16    #[token("{external}")]
17    PropertyExternal,
18
19    #[token("{ref}")]
20    PropertyByRef,
21
22    #[token("@CONSTANT")]
23    #[token("{constant}")]
24    PropertyConstant,
25
26    #[token("{sized}")]
27    PropertySized,
28
29    #[token("PROGRAM", ignore(case))]
30    KeywordProgram,
31
32    #[token("CLASS", ignore(case))]
33    KeywordClass,
34
35    #[token("END_CLASS", ignore(case))]
36    #[token("ENDCLASS", ignore(case))]
37    KeywordEndClass,
38
39    #[token("EXTENDS", ignore(case))]
40    KeywordExtends,
41
42    #[token("IMPLEMENTS", ignore(case))]
43    KeywordImplements,
44
45    #[token("INTERFACE", ignore(case))]
46    KeywordInterface,
47
48    #[token("END_INTERFACE", ignore(case))]
49    #[token("ENDINTERFACE", ignore(case))]
50    KeywordEndInterface,
51
52    #[token("VAR_INPUT", ignore(case))]
53    #[token("VARINPUT", ignore(case))]
54    KeywordVarInput,
55
56    #[token("VAR_OUTPUT", ignore(case))]
57    #[token("VAROUTPUT", ignore(case))]
58    KeywordVarOutput,
59
60    #[token("VAR", ignore(case))]
61    KeywordVar,
62
63    #[token("VAR_CONFIG", ignore(case))]
64    KeywordVarConfig,
65
66    #[token("ABSTRACT", ignore(case))]
67    KeywordAbstract,
68
69    #[token("FINAL", ignore(case))]
70    KeywordFinal,
71
72    #[token("METHOD", ignore(case))]
73    KeywordMethod,
74
75    #[token("END_METHOD", ignore(case))]
76    #[token("ENDMETHOD", ignore(case))]
77    KeywordEndMethod,
78
79    #[token("SUPER", ignore(case))]
80    KeywordSuper,
81
82    #[token("THIS", ignore(case))]
83    KeywordThis,
84
85    #[token("PROPERTY_GET", ignore(case))]
86    #[token("PROPERTYGET", ignore(case))]
87    KeywordPropertyGet,
88
89    #[token("PROPERTY_SET", ignore(case))]
90    #[token("PROPERTYSET", ignore(case))]
91    KeywordPropertySet,
92
93    #[token("END_PROPERTY", ignore(case))]
94    #[token("ENDPROPERTY", ignore(case))]
95    KeywordEndProperty,
96
97    #[token("CONSTANT", ignore(case))]
98    KeywordConstant,
99
100    #[token("RETAIN", ignore(case))]
101    KeywordRetain,
102
103    #[token("NON_RETAIN", ignore(case))]
104    #[token("NONRETAIN", ignore(case))]
105    KeywordNonRetain,
106
107    #[token("VAR_TEMP", ignore(case))]
108    #[token("VARTEMP", ignore(case))]
109    KeywordVarTemp,
110
111    #[token("PUBLIC", ignore(case))]
112    KeywordAccessPublic,
113
114    #[token("PRIVATE", ignore(case))]
115    KeywordAccessPrivate,
116
117    #[token("INTERNAL", ignore(case))]
118    KeywordAccessInternal,
119
120    #[token("PROTECTED", ignore(case))]
121    KeywordAccessProtected,
122
123    #[token("OVERRIDE", ignore(case))]
124    KeywordOverride,
125
126    #[token("VAR_GLOBAL", ignore(case))]
127    #[token("VARGLOBAL", ignore(case))]
128    KeywordVarGlobal,
129
130    #[token("VAR_IN_OUT", ignore(case))]
131    #[token("VARINOUT", ignore(case))]
132    KeywordVarInOut,
133
134    #[token("VAR_EXTERNAL", ignore(case))]
135    KeywordVarExternal,
136
137    #[token("END_VAR", ignore(case))]
138    #[token("ENDVAR", ignore(case))]
139    KeywordEndVar,
140
141    #[token("END_PROGRAM", ignore(case))]
142    #[token("ENDPROGRAM", ignore(case))]
143    KeywordEndProgram,
144
145    #[token("FUNCTION", ignore(case))]
146    KeywordFunction,
147
148    #[token("END_FUNCTION", ignore(case))]
149    #[token("ENDFUNCTION", ignore(case))]
150    KeywordEndFunction,
151
152    #[token("FUNCTION_BLOCK", ignore(case))]
153    #[token("FUNCTIONBLOCK", ignore(case))]
154    KeywordFunctionBlock,
155
156    #[token("END_FUNCTION_BLOCK", ignore(case))]
157    #[token("ENDFUNCTIONBLOCK", ignore(case))]
158    KeywordEndFunctionBlock,
159
160    #[token("TYPE", ignore(case))]
161    KeywordType,
162
163    #[token("STRUCT", ignore(case))]
164    KeywordStruct,
165
166    #[token("END_TYPE", ignore(case))]
167    #[token("ENDTYPE", ignore(case))]
168    KeywordEndType,
169
170    #[token("END_STRUCT", ignore(case))]
171    #[token("ENDSTRUCT", ignore(case))]
172    KeywordEndStruct,
173
174    #[token("ACTIONS", ignore(case))]
175    KeywordActions,
176
177    #[token("ACTION", ignore(case))]
178    KeywordAction,
179
180    #[token("END_ACTION", ignore(case))]
181    #[token("ENDACTION", ignore(case))]
182    KeywordEndAction,
183
184    #[token("END_ACTIONS", ignore(case))]
185    #[token("ENDACTIONS", ignore(case))]
186    KeywordEndActions,
187
188    #[token(":")]
189    KeywordColon,
190
191    #[token(";")]
192    KeywordSemicolon,
193
194    #[token(":=")]
195    KeywordAssignment,
196
197    #[token("=>")]
198    KeywordOutputAssignment,
199
200    #[token("REF=", ignore(case))]
201    KeywordReferenceAssignment,
202
203    #[token("(")]
204    KeywordParensOpen,
205
206    #[token(")")]
207    KeywordParensClose,
208
209    #[token("[")]
210    KeywordSquareParensOpen,
211
212    #[token("]")]
213    KeywordSquareParensClose,
214
215    #[token(",")]
216    KeywordComma,
217
218    #[token("...")]
219    KeywordDotDotDot,
220
221    #[token("..")]
222    KeywordDotDot,
223
224    #[token(".")]
225    KeywordDot,
226
227    //Control Structures
228    #[token("IF", ignore(case))]
229    KeywordIf,
230
231    #[token("THEN", ignore(case))]
232    KeywordThen,
233
234    #[token("ELSIF", ignore(case))]
235    KeywordElseIf,
236
237    #[token("ELSE", ignore(case))]
238    KeywordElse,
239
240    #[token("END_IF", ignore(case))]
241    #[token("ENDIF", ignore(case))]
242    KeywordEndIf,
243
244    #[token("FOR", ignore(case))]
245    KeywordFor,
246
247    #[token("TO", ignore(case))]
248    KeywordTo,
249
250    #[token("BY", ignore(case))]
251    KeywordBy,
252
253    #[token("DO", ignore(case))]
254    KeywordDo,
255
256    #[token("END_FOR", ignore(case))]
257    #[token("ENDFOR", ignore(case))]
258    KeywordEndFor,
259
260    #[token("WHILE", ignore(case))]
261    KeywordWhile,
262
263    #[token("END_WHILE", ignore(case))]
264    #[token("ENDWHILE", ignore(case))]
265    KeywordEndWhile,
266
267    #[token("REPEAT", ignore(case))]
268    KeywordRepeat,
269
270    #[token("UNTIL", ignore(case))]
271    KeywordUntil,
272
273    #[token("END_REPEAT", ignore(case))]
274    #[token("ENDREPEAT", ignore(case))]
275    KeywordEndRepeat,
276
277    #[token("CASE", ignore(case))]
278    KeywordCase,
279
280    #[token("RETURN", ignore(case))]
281    KeywordReturn,
282
283    #[token("EXIT", ignore(case))]
284    KeywordExit,
285
286    #[token("CONTINUE", ignore(case))]
287    KeywordContinue,
288
289    #[token("POINTER", ignore(case))]
290    KeywordPointer,
291
292    #[token("__FPOINTER", ignore(case))]
293    KeywordFunctionPointer,
294
295    #[token("REF_TO", ignore(case))]
296    #[token("REFTO", ignore(case))]
297    KeywordRef,
298
299    #[token("REFERENCE TO", ignore(case))]
300    KeywordReferenceTo,
301
302    #[token("ARRAY", ignore(case))]
303    KeywordArray,
304
305    #[token("STRING", ignore(case))]
306    KeywordString,
307
308    #[token("WSTRING", ignore(case))]
309    KeywordWideString,
310
311    #[token("OF", ignore(case))]
312    KeywordOf,
313
314    #[token("AT", ignore(case))]
315    KeywordAt,
316
317    #[token("END_CASE", ignore(case))]
318    #[token("ENDCASE", ignore(case))]
319    KeywordEndCase,
320
321    //Operators
322    #[token("+")]
323    OperatorPlus,
324
325    #[token("-")]
326    OperatorMinus,
327
328    #[token("*")]
329    OperatorMultiplication,
330
331    #[token("**")]
332    OperatorExponent,
333
334    #[token("/")]
335    OperatorDivision,
336
337    #[token("=")]
338    OperatorEqual,
339
340    #[token("<>")]
341    OperatorNotEqual,
342
343    #[token("<")]
344    OperatorLess,
345
346    #[token(">")]
347    OperatorGreater,
348
349    #[token("<=")]
350    OperatorLessOrEqual,
351
352    #[token(">=")]
353    OperatorGreaterOrEqual,
354
355    #[token("&")]
356    OperatorAmp,
357
358    #[token("^")]
359    OperatorDeref,
360
361    #[token("MOD", ignore(case))]
362    OperatorModulo,
363
364    #[token("AND_THEN", ignore(case))]
365    OperatorAndThen,
366
367    #[token("AND", ignore(case))]
368    OperatorAnd,
369
370    #[token("OR_ELSE", ignore(case))]
371    OperatorOrElse,
372
373    #[token("OR", ignore(case))]
374    OperatorOr,
375
376    #[token("XOR", ignore(case))]
377    OperatorXor,
378
379    #[token("NOT", ignore(case))]
380    OperatorNot,
381
382    //Identifiers
383    #[regex(r"[a-zA-Z_][a-zA-Z_0-9]*")]
384    Identifier,
385
386    //Literals
387    #[regex(r"16#[0-9a-fA-F]+(_[0-9a-fA-F]+)*")]
388    LiteralIntegerHex,
389
390    #[regex(r"8#[0-7]+(_[0-7]+)*")]
391    LiteralIntegerOct,
392
393    #[regex(r"2#[0-1]+(_[0-1]+)*")]
394    LiteralIntegerBin,
395
396    #[regex(r"[0-9]+(_[0-9]+)*([eE][+-]?[0-9]+)?")]
397    LiteralInteger,
398
399    #[token("NULL", ignore(case))]
400    LiteralNull,
401
402    #[token("TRUE", ignore(case))]
403    LiteralTrue,
404
405    #[token("FALSE", ignore(case))]
406    LiteralFalse,
407
408    #[regex("(LDATE|DATE|LD|D)#\\d+-\\d+-\\d+", ignore(case))]
409    LiteralDate,
410
411    #[regex(
412        "(DATE_AND_TIME|DT|LDATE_AND_TIME|LDT)#\\d+-\\d+-\\d+-\\d+:\\d+(:\\d+(\\.\\d+)?)?",
413        ignore(case)
414    )]
415    LiteralDateAndTime,
416
417    #[regex("(TIME_OF_DAY|LTIME_OF_DAY|TOD|LTOD)#\\d+:\\d+(:\\d+(\\.\\d+)?)?", ignore(case))]
418    LiteralTimeOfDay,
419
420    #[regex("(LTIME|LT|TIME|T)#-?(\\d+(\\.\\d+)?(d|h|ms|m|s|us|ns))+", ignore(case))]
421    LiteralTime,
422
423    #[regex("%(B|b|D|d|W|w|L|l|X|x)", super::parse_access_type)]
424    DirectAccess(DirectAccessType),
425
426    #[regex(r"%(I|i|Q|q|M|m|G|g)(B|b|D|d|W|w|L|l|X|x|\*)", super::parse_hardware_access_type)]
427    HardwareAccess((HardwareAccessType, DirectAccessType)),
428
429    #[regex("'((\\$.)|[^$'])*'")]
430    LiteralString,
431
432    #[regex("\"((\\$.)|[^$\"])*\"")]
433    LiteralWideString,
434
435    #[regex("[a-zA-Z_][a-zA-Z_0-9]*#")]
436    TypeCastPrefix,
437
438    #[regex(r"[ \t\n\f]+", logos::skip)]
439    End,
440}
441
442impl Token {
443    /// Returns true if the current token represents any `VAR(_*)` keyword
444    pub fn is_var(&self) -> bool {
445        matches!(
446            self,
447            Token::KeywordVar
448                | Token::KeywordVarInput
449                | Token::KeywordVarOutput
450                | Token::KeywordVarInOut
451                | Token::KeywordVarTemp
452        )
453    }
454
455    /// Returns true if the current token can be used where an identifier is expected.
456    ///
457    /// `PROPERTY_GET` and `PROPERTY_SET` are soft keywords: they are parsed as property accessors in
458    /// property declarations, but may still be used as identifiers elsewhere.
459    pub fn is_identifier_like(&self) -> bool {
460        matches!(self, Token::Identifier | Token::KeywordPropertyGet | Token::KeywordPropertySet)
461    }
462
463    /// Linguistic classification of this token, returned as a slice so a
464    /// token can carry more than one class. Empty when nothing currently
465    /// classifies the token.
466    pub fn classes(&self) -> &'static [TokenClass] {
467        use TokenClass::*;
468        match self {
469            Token::KeywordConstant
470            | Token::KeywordRetain
471            | Token::KeywordNonRetain
472            | Token::KeywordAbstract
473            | Token::KeywordFinal
474            | Token::KeywordOverride
475            | Token::KeywordAccessPublic
476            | Token::KeywordAccessPrivate
477            | Token::KeywordAccessInternal
478            | Token::KeywordAccessProtected => &[Modifier],
479
480            Token::KeywordExit => &[ControlFlowJump],
481
482            Token::KeywordString | Token::KeywordWideString => &[BuiltinDatatype],
483
484            _ => &[],
485        }
486    }
487}
488
489#[derive(Debug, Clone, Copy, PartialEq, Eq)]
490pub enum TokenClass {
491    /// Declaration modifier that qualifies a following construct.
492    Modifier,
493    /// Standalone control-flow jump statement.
494    ControlFlowJump,
495    /// Built-in datatype keyword.
496    BuiltinDatatype,
497}