nodalis-compiler
Version:
Compiles IEC-61131-3/10 languages into code that can be used as a PLC on multiple platforms.
79 lines (66 loc) • 2.78 kB
JavaScript
/* eslint-disable curly */
/* eslint-disable eqeqeq */
// Copyright [2025] Nathan Skipper
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @description Structured Text Tokenizer
* @author Nathan Skipper, MTI
* @version 1.0.2
* @copyright Apache 2.0
*/
/**
* Tokenizes a block of structured text into their types and values.
* @param {string} code A block of structured text code.
* @returns {{type: string, value: string}[]} An array of tokens.
*/
export function tokenize(code) {
const tokens = [];
let match;
// Remove single-line comments (//...)
code = code.replace(/\/\/.*$/gm, '');
// Remove multi-line comments ((*...*))
code = code.replace(/\(\*[\s\S]*?\*\)/g, '');
//const regex = /(%[IQM][A-Z]?[0-9]+(?:\.[0-9]+)?)|(:=)|([A-Za-z_]\w*\.\d+)|([A-Za-z_]\w*\.\w+)|([A-Za-z_]\w*)|(\d+)|([:;()<>+\-*/=])/g;
const regex = /(%[IQM][A-Z]*\d+(?:\.\d+)?)|(:=|>=|<=|<>|!=)|([A-Za-z_]\w*\.\d+)|([A-Za-z_]\w*\.\w+)|([A-Za-z_]\w*)|(\d+)|([<>+\-*/=;():])/g;
while ((match = regex.exec(code)) !== null) {
const [_, address, compoundSymbol, bitIdentifier, propIdentifier, identifier, number, symbol] = match;
if (address)
tokens.push({ type: 'ADDRESS', value: address });
else if (compoundSymbol)
tokens.push({ type: 'SYMBOL', value: compoundSymbol });
else if (bitIdentifier)
tokens.push({ type: 'IDENTIFIER', value: bitIdentifier });
else if (propIdentifier)
tokens.push({ type: 'IDENTIFIER', value: propIdentifier });
else if (identifier)
tokens.push({ type: 'IDENTIFIER', value: identifier });
else if (number)
tokens.push({ type: 'NUMBER', value: number });
else if (symbol)
tokens.push({ type: 'SYMBOL', value: symbol });
}
return tokens;
}
function getTokenType(value) {
const keywords = new Set([
'PROGRAM', 'FUNCTION_BLOCK', 'FUNCTION', 'VAR_INPUT', 'VAR_OUTPUT', 'VAR', 'END_VAR',
'END_FUNCTION_BLOCK', 'END_FUNCTION', 'END_PROGRAM'
]);
const symbols = new Set([':=', ';', ':', '(', ')', '+', '-', '*', '/', '>', '<', '=']);
if (keywords.has(value.toUpperCase())) return 'KEYWORD';
if (symbols.has(value)) return 'SYMBOL';
if (/^\d+$/.test(value)) return 'NUMBER';
if (/^[A-Za-z_]\w*$|(%[IQM][\d.]+)/.test(value)) return 'IDENTIFIER';
return 'UNKNOWN';
}