-
Notifications
You must be signed in to change notification settings - Fork 740
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add lexer for IEC 61131-3 Structured Text
The new lexer uses sets of keywords to quickly categorize names and uses regular expressions for other elements like numbers and punctuation.
- Loading branch information
Showing
6 changed files
with
194 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// first-order lag derivative term | ||
(* | ||
Parameter K must be set to T / (T + T1) | ||
*) | ||
FUNCTION_BLOCK DT2 | ||
|
||
VAR_INPUT | ||
IN : REAL; | ||
K : REAL; | ||
END_VAR | ||
VAR_OUTPUT | ||
Q : REAL; | ||
END_VAR | ||
VAR | ||
H : REAL := 0.0; | ||
END_VAR | ||
|
||
BEGIN | ||
Q := IN - H; | ||
H := H + K * Q; | ||
END_FUNCTION_BLOCK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# -*- coding: utf-8 -*- # | ||
# frozen_string_literal: true | ||
|
||
module Rouge | ||
module Lexers | ||
class IecST < RegexLexer | ||
tag 'iecst' | ||
title "IEC 61131-3 Structured Text" | ||
desc 'Structured text is a programming language for PLCs (programmable logic controllers).' | ||
filenames '*.awl', '*.scl', '*.st' | ||
|
||
mimetypes 'text/x-iecst' | ||
|
||
def self.keywords | ||
blocks = %w( | ||
PROGRAM CONFIGURATION INITIAL_STEP INTERFACE FUNCTION_BLOCK FUNCTION ACTION TRANSITION | ||
TYPE STRUCT STEP NAMESPACE LIBRARY CHANNEL FOLDER RESOURCE | ||
VAR_ACCESS VAR_CONFIG VAR_EXTERNAL VAR_GLOBAL VAR_INPUT VAR_IN_OUT VAR_OUTPUT VAR_TEMP VAR | ||
CONST METHOD PROPERTY | ||
CASE FOR IF REPEAT WHILE | ||
) | ||
@keywords ||= Set.new %w( | ||
AT BEGIN BY CONSTANT CONTINUE DO ELSE ELSIF EXIT EXTENDS FROM GET GOTO IMPLEMENTS JMP | ||
NON_RETAIN OF PRIVATE PROTECTED PUBLIC RETAIN RETURN SET TASK THEN TO UNTIL USING WITH | ||
__CATCH __ENDTRY __FINALLY __TRY | ||
) + blocks + blocks.map {|kw| "END_" + kw} | ||
end | ||
|
||
def self.types | ||
@types ||= Set.new %w( | ||
ANY ARRAY BOOL BYTE POINTER STRING | ||
DATE DATE_AND_TIME DT TIME TIME_OF_DAY TOD | ||
INT DINT LINT SINT UINT UDINT ULINT USINT | ||
WORD DWORD LWORD | ||
REAL LREAL | ||
) | ||
end | ||
|
||
def self.literals | ||
@literals ||= Set.new %w(TRUE FALSE NULL) | ||
end | ||
|
||
def self.operators | ||
@operators ||= Set.new %w(AND EQ EXPT GE GT LE LT MOD NE NOT OR XOR) | ||
end | ||
|
||
state :whitespace do | ||
# Spaces | ||
rule %r/\s+/m, Text | ||
# // Comments | ||
rule %r((//).*$\n?), Comment::Single | ||
# (* Comments *) | ||
rule %r(\(\*.*?\*\))m, Comment::Multiline | ||
# { Comments } | ||
rule %r(\{.*?\})m, Comment::Special | ||
end | ||
|
||
state :root do | ||
mixin :whitespace | ||
|
||
rule %r/'[^']+'/, Literal::String::Single | ||
rule %r/"[^"]+"/, Literal::String::Symbol | ||
rule %r/%[IQM][XBWDL][\d.]*|%[IQ][\d.]*/, Name::Variable::Magic | ||
rule %r/\b(?:D|DT|T|TOD)#[\d_shmd:]*/i, Literal::Date | ||
rule %r/\b(?:16#[\d_a-f]+|0x[\d_a-f]+)\b/i, Literal::Number::Hex | ||
rule %r/\b2#[01_]+/, Literal::Number::Bin | ||
rule %r/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i, Literal::Number::Float | ||
rule %r/\b[\d.,_]+/, Literal::Number | ||
|
||
rule %r/\b[A-Z_]+\b/i do |m| | ||
name = m[0].upcase | ||
if self.class.keywords.include?(name) | ||
token Keyword | ||
elsif self.class.types.include?(name) | ||
token Keyword::Type | ||
elsif self.class.literals.include?(name) | ||
token Literal | ||
elsif self.class.operators.include?(name) | ||
token Operator | ||
else | ||
token Name | ||
end | ||
end | ||
|
||
rule %r/S?R?:?=>?|&&?|\*\*?|<[=>]?|>=?|[-:^\/+#]/, Operator | ||
rule %r/\b[a-z_]\w*(?=\s*\()/i, Name::Function | ||
rule %r/\b[a-z_]\w*\b/i, Name | ||
rule %r/[()\[\].,;]/, Punctuation | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# -*- coding: utf-8 -*- # | ||
# frozen_string_literal: true | ||
|
||
describe Rouge::Lexers::IecST do | ||
let(:subject) { Rouge::Lexers::IecST.new } | ||
|
||
describe 'guessing' do | ||
include Support::Guessing | ||
|
||
it 'guesses by filename' do | ||
assert_guess :filename => 'foo.awl' | ||
assert_guess :filename => 'foo.scl' | ||
assert_guess :filename => 'foo.st', :source => "VAR ; END_VAR;" | ||
end | ||
|
||
it 'guesses by mimetype' do | ||
assert_guess :mimetype => 'text/x-iecst' | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// single line comment | ||
(* | ||
Block comment | ||
*) | ||
FUNCTION_BLOCK SAMPLE | ||
TITLE='Sample function block' | ||
VERSION : '1.0' | ||
AUTHOR : 'Rouge' | ||
FAMILY: 'sample' | ||
|
||
CONST | ||
Thousand : INT := 1_000; | ||
TwoFiftyFive : BYTE := BYTE#255; | ||
InHex : BYTE := 16#FF; | ||
WithType : BYTE := BYTE#16#FF; | ||
InBinary : BYTE := 2#1111_1111; | ||
|
||
PI : REAL := 3.141592; | ||
OneBillion : REAL := 1_000_000_000; | ||
WithExponent : REAL := REAL#1e+9; | ||
END_CONST | ||
|
||
VAR_INPUT | ||
a : REAL; | ||
b : REAL; | ||
limit : REAL; | ||
END_VAR | ||
VAR_OUTPUT | ||
valid : BOOL; | ||
END_VAR | ||
|
||
VAR | ||
last: REAL := 0.0; | ||
END_VAR | ||
|
||
VAR_TEMP | ||
value: REAL; | ||
END_VAR | ||
|
||
BEGIN | ||
IF a > 0 AND b > 0 THEN | ||
value := sum / 2; | ||
ELSIF a > 0 THEN | ||
value := a; | ||
ELSIF b > 0 THEN | ||
value := b; | ||
ELSE | ||
value := last; | ||
END_IF | ||
last := value; | ||
|
||
valid := value < limit; | ||
END_FUNCTION_BLOCK |