In one of his posts Maciej Anisierowicz recommended SyntaxHighlighter Evolved plugin, and indeed it’s a great one if you want to highlight one of popular languages syntax. In case of niche languages, like ones defined in IEC 61131-3, one has to implement his own language extension for SyntaxHighlighter.
IEC 61131-3 Structured Text
Due to #dajsiepoznac project there will appear Structured Text samples. Even though it resembles Pascal on which is based, included pascal syntax falls short.
(* Note samples based on ones available at http://infosys.beckhoff.com/ *) TYPE TSimpleStruct : STRUCT lrealVal: LREAL := 1.23; dintVal1: DINT := 120000; END_STRUCT END_TYPE TYPE TComplexStruct : STRUCT intVal : INT:=1200; dintArr: ARRAY[0..3] OF DINT:= 1,2,3,4; boolVal: BOOL := FALSE; byteVal: BYTE:=10; stringVal : STRING(5) := 'hallo'; simpleStruct1: TSimpleStruct; END_STRUCT END_TYPE PROGRAM MAIN VAR REAL32_1 AT %MB0 : REAL; (* 1 *) REAL32_2 AT %MB4 : REAL; (* 2 *) REAL32_3 AT %MB8 : REAL; (* 3 *) REAL32_4 AT %MB12: REAL; (* 4 *) REAL32_5 AT %MB16: REAL; (* 5 *) PLCVar : ARRAY [0..99] OF INT; Index: BYTE; text : STRING[30] := 'hello'; Time1:TIME := T#21h33m23s231ms; Time2:TIME := T#21H; Time3:TIME := T#33M21MS; DateTime1:DT:=DT#1993-06-12-15:36:55.40; tod1:TOD:=TIME_OF_DAY#15:36:55.40; Date1:DATE:=DATE#1993-06-12; Bool1:BOOL := FALSE; int1:INT := 30000; dint1:DINT:=125000; usint1:USINT:=200; real1:REAL:= 1.2; lreal1:LREAL:=3.5; str1:STRING := 'this is a test string'; str2:STRING(5) := 'hallo'; complexStruct1 : TComplexStruct; END_VAR FOR Index := 0 TO 99 DO PLCVar[Index] := 3500 + INDEX; END_FOR
(* Note samples based on ones available at http://infosys.beckhoff.com/ *) TYPE TSimpleStruct : STRUCT lrealVal: LREAL := 1.23; dintVal1: DINT := 120000; END_STRUCT END_TYPE TYPE TComplexStruct : STRUCT intVal : INT:=1200; dintArr: ARRAY[0..3] OF DINT:= 1,2,3,4; boolVal: BOOL := FALSE; byteVal: BYTE:=10; stringVal : STRING(5) := 'hallo'; simpleStruct1: TSimpleStruct; END_STRUCT END_TYPE PROGRAM MAIN VAR REAL32_1 AT %MB0 : REAL; (* 1 *) REAL32_2 AT %MB4 : REAL; (* 2 *) REAL32_3 AT %MB8 : REAL; (* 3 *) REAL32_4 AT %MB12: REAL; (* 4 *) REAL32_5 AT %MB16: REAL; (* 5 *) PLCVar : ARRAY [0..99] OF INT; Index: BYTE; text : STRING[30] := 'hello'; Time1:TIME := T#21h33m23s231ms; Time2:TIME := T#21H; Time3:TIME := T#33M21MS; DateTime1:DT:=DT#1993-06-12-15:36:55.40; tod1:TOD:=TIME_OF_DAY#15:36:55.40; Date1:DATE:=DATE#1993-06-12; Bool1:BOOL := FALSE; int1:INT := 30000; dint1:DINT:=125000; usint1:USINT:=200; real1:REAL:= 1.2; lreal1:LREAL:=3.5; str1:STRING := 'this is a test string'; str2:STRING(5) := 'hallo'; complexStruct1 : TComplexStruct; END_VAR FOR Index := 0 TO 99 DO PLCVar[Index] := 3500 + INDEX; END_FOR
Extending syntax highlighter
WP plugin SyntaxHighlighter Evolved is based on SyntaxHighlighter JavaScript package by Alex Gorbatchev, which is OSS available on GitHub. Thus we’ll be developing plugin for both. Best way to start is to find a supported language that most resembles yours.
JS extension
var BrushBase = require('brush-base'); var regexLib = require('syntaxhighlighter-regex').commonRegExp; function Brush() { var keywords = 'ABS ACOS ACTION ADD AND ANDN ANY ANY_BIT ANY_DATE ANY_INT ANY_NUM ANY_REAL ARRAY ASIN AT ATAN ' + 'BOOL BY BYTE ' + 'CAL CALC CALCN CASE CD CDT CLK CONCAT CONFIGURATION CONSTANT COS CTD CTU CTUD CU CV ' + 'D DATE DATE_AND_TIME DELETE DINT DIV DO DS DT DWORD '+ 'ELSE ELSIF END_ACTION END_CASE END_CONFIGURATION END_FOR END_FUNCTION END_FUNCTION_BLOCK END_IF END_PROGRAM END_REPEAT END_RESOURCE END_STEP END_STRUCT END_TRANSITION END_TYPE END_VAR END_WHILE EN ENO EQ ET EXIT EXP EXPT '+ 'FALSE F_EDGE F_TRIG FIND FOR FROM FUNCTION FUNCTION_BLOCK '+ 'GE GT'+ 'IF IN INITIAL_STEP INSERT INT INTERVAL '+ 'JMP JMPC JMPCN '+ 'L LD LDN LE LEFT LEN LIMIT LINT LN LOG LREAL LT LWORD '+ 'MAX MID MIN MOD MOVE MUL MUX '+ 'N NE NEG NOT '+ 'OF ON OR OEN '+ 'P PRIORITY PROGRAM PT PV '+ 'Q Q1 QU QD '+ 'R R1 R_TRIG READ_ONLY READ_WRITE REAL RELEASE REPEAT REPLACE RESOURCE RET RETAIN RETC RTCN RETURN RIGHT ROL ROR RS RTC R_EDGE '+ 'S S1 SD SEL SEMA SHL SHR SIN SINGLE SINT SL SQRT SR ST STEP STN STRING STRUCT SUB '+ 'TAN TASK THEN TIME TIME_OF_DAY TO TOD TOF TON TP TRANSITION TRUE TYPE '+ 'UDINT UINT ULINT UNTIL USINT '+ 'VAR VAR_ACCESS VAR_EXTERNAL VAR_GLOBAL VAR_INPUT VAR_IN_OUT VAR_OUTPUT '+ 'WHILE WITH WORD '+ 'XOR XORN'; this.regexList = [ { //time literal regex: /(T|t|TIME|time)(?=.*([hms]|[HMS]))#(\d+(h|H))?(\d+(m|M))?(\d+(s|s))?(\d+(ms|MS))?/g, css: 'color2' }, { // date and time literal regex: /(DT|dt|date_and_time|DATE_AND_TIME)#\d{4}-\d{2}-\d{2}-\d{2}:\d{2}:\d{2}\.\d{2}/g, css: 'color2' }, { // time of day literal regex: /(TOD|tod|time_of_day|TIME_OF_DAY)#\d+:\d+(:\d+)?((\.\d+)|(\.?))/g, css: 'color2' }, { //date literal regex: /(D|d|DATE|date)#\d{4}-\d{2}-\d{2}/g, css: 'color2' }, { //direct memory adressing regex: /%[A-Z]{1,2}\d+(\.\d+)*/g, css: 'color2' }, { //multiline comment (* *) regex: /\(\*[\s\S]*?\*\)/gm, css: 'comments' }, { //string literal 'myvalue' regex: regexLib.singleQuotedString, css: 'string' }, { //number integers, floating point with dot or exponential regex: /\b\d+([\.eE]\d+)?\b/g, css: 'value' }, { //keywords regex: new RegExp(this.getKeywords(keywords), 'gmi'), css: 'keyword' }]; }; Brush.prototype = new BrushBase(); Brush.aliases = ['structuredtext', 'ST', 'IEC61131','st','iec61131']; module.exports = Brush;
on lines 5-26 you may find keywords defined by IEC 61131-3 standard, later used to construct regex, in order to be highlighted, at line 76 which are later used at line. Beside using predefined single quoted string (line 61), we define regexes for language specific futures like TIME/DATE and direct memory addressing.
Make it work with WordPress
To use it at WordPress based site, we need to make it a plugin, extending SyntaxHighlighter Evolved. Separate plugin, as we don’t want to lose our changes on update of SH.
SyntaxHighlighter.brushes.IEC61131 = function() //keywords as specified by IEC 61131-3 var keywords = 'ABS ACOS ACTION ADD AND ANDN ANY ANY_BIT ANY_DATE ANY_INT ANY_NUM ANY_REAL ARRAY ASIN AT ATAN '; /*rest of keywords*/ this.regexList = [ { //time literal regex: /(T|t|TIME|time)(?=.*([hms]|[HMS]))#(\d+(h|H))?(\d+(m|M))?(\d+(s|s))?(\d+(ms|MS))?/g, css: 'color2' }, /* other custom regexes */ { //string literal 'myvalue' regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' } ]; }; SyntaxHighlighter.brushes.IEC61131.prototype = new SyntaxHighlighter.Highlighter(); SyntaxHighlighter.brushes.IEC61131.aliases = ['structuredtext', 'ST', 'IEC61131', 'iec61131', 'st'];
Highlighted are main differences, note that we’re not using Brush, as before but SyntaxHighlighter.brushes.IEC61131 instead.
<?php /*put the usual WordPress plugin info here*/ //hook to init add_action( 'init', 'syntaxhighlighter_IEC61131_regscript' ); // register brushe function syntaxhighlighter_IEC61131_regscript() { wp_register_script( 'syntaxhighlighter-brush-iec61131', plugins_url( 'IEC61131-brush.js', __FILE__ ), array('syntaxhighlighter-core'), '1.2.3' ); } // add filter for aliases add_filter( 'syntaxhighlighter_brushes', 'syntaxhighlighter_IEC61131_brush' ); function syntaxhighlighter_IEC61131_brush($brushes) { $brushes['iec61131'] = 'iec61131'; $brushes['IEC61131'] = $brushes['iec61131']; $brushes['structuredtext'] = $brushes['iec61131']; $brushes['ST'] = $brushes['iec61131']; $brushes['st'] = $brushes['iec61131']; return $brushes; } ?>
Things we do in our litle plugin:
- on line 5 we hook to WordPress init in order to
- register our js script and it’s dependencies on line 9
- on line 13 we register to SyntaxHighlighter Evolved filter for brushes in order to make it aware of our existance
- finally we register our aliases (remember last line of our js plugin?) so that we could use them as shortcodes
And that’s pretty it when it comes to implement custom syntax plugin. It takes less time to create plugin than to write about it…
PS. I always wanted to release something under WTF PL 🙂