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 🙂