Files
tia-lsp/grammar.js
T
2026-02-14 15:05:12 +01:00

383 lines
9.1 KiB
JavaScript

module.exports = grammar({
name: 'scl',
extras: $ => [
/\s/,
$.line_comment,
$.block_comment,
$.c_style_comment
],
conflicts: $ => [
[$.case_item],
[$.region_statement],
[$.region_name],
[$.case_statement],
[$.var_declaration, $.var_temp_declaration],
[$.var_declaration, $.var_constant_declaration],
[$.var_declaration, $.var_retain_declaration],
[$.var_declaration, $.var_non_retain_declaration]
],
rules: {
source_file: $ => repeat(
choice(
$.organization_block,
$.function_block,
$.function,
$.statement
)
),
organization_block: $ => seq(
'ORGANIZATION_BLOCK',
field('name', $.string),
optional(seq('TITLE', '=', choice($.string, $.identifier))),
optional($.attribute_list),
optional(seq('VERSION', ':', $.number)),
repeat($.var_declaration),
'BEGIN',
repeat($.statement),
'END_ORGANIZATION_BLOCK'
),
function_block: $ => seq(
'FUNCTION_BLOCK',
field('name', $.string),
optional(seq('TITLE', '=', choice($.string, $.identifier))),
optional($.attribute_list),
optional(seq('VERSION', ':', $.number)),
repeat($.var_declaration),
'BEGIN',
repeat($.statement),
'END_FUNCTION_BLOCK'
),
function: $ => seq(
'FUNCTION',
field('name', $.string),
optional(seq(':', $.type)),
optional(seq('TITLE', '=', choice($.string, $.identifier))),
optional($.attribute_list),
optional(seq('VERSION', ':', $.number)),
repeat(choice(
$.var_declaration,
$.var_temp_declaration,
$.var_constant_declaration,
$.var_retain_declaration,
$.var_non_retain_declaration,
$.block_comment
)),
'BEGIN',
repeat($.statement),
'END_FUNCTION'
),
attribute_list: $ => seq(
'{',
$.attribute,
repeat(seq(choice(',', ';'), $.attribute)),
optional(choice(',', ';')),
'}'
),
attribute: $ => seq(
$.identifier,
':=',
$.attribute_value
),
attribute_value: $ => choice(
$.string,
'TRUE', 'FALSE',
$.identifier
),
var_declaration: $ => seq(
choice(
'VAR_INPUT',
'VAR_OUTPUT',
'VAR_IN_OUT',
'VAR',
'VAR_TEMP',
seq('VAR', 'CONSTANT'),
seq('VAR', 'RETAIN'),
seq('VAR', 'NON_RETAIN'),
'VAR_CONSTANT',
'VAR_RETAIN',
'VAR_NON_RETAIN'
),
repeat(choice($.var_item, $.block_comment)),
'END_VAR'
),
var_temp_declaration: $ => seq(
'VAR_TEMP',
repeat(choice($.var_item, $.block_comment)),
'END_VAR'
),
var_constant_declaration: $ => seq(
choice(
seq('VAR', 'CONSTANT'),
'VAR_CONSTANT'
),
repeat(choice($.var_item, $.block_comment)),
'END_VAR'
),
var_retain_declaration: $ => seq(
choice(
seq('VAR', 'RETAIN'),
'VAR_RETAIN'
),
repeat(choice($.var_item, $.block_comment)),
'END_VAR'
),
var_non_retain_declaration: $ => seq(
choice(
seq('VAR', 'NON_RETAIN'),
'VAR_NON_RETAIN'
),
repeat(choice($.var_item, $.block_comment)),
'END_VAR'
),
var_item: $ => seq(
field('name', $.identifier),
optional($.attribute_list),
':',
field('type', $.type),
optional(seq(':=', $.expression)),
';'
),
type: $ => choice(
'BOOL', 'Bool',
'INT', 'Int', 'int',
'DINT', 'DInt', 'dint',
'SINT', 'SInt', 'sint',
'USINT', 'USInt', 'usint',
'UINT', 'UInt', 'uint',
'WORD', 'Word', 'word',
'DWORD', 'DWord', 'dword',
'BYTE', 'Byte', 'byte',
'REAL', 'Real', 'real',
'LREAL', 'LReal', 'lreal',
'TIME', 'Time', 'time',
'TOD', 'Tod', 'tod',
'DT', 'Dt', 'dt',
'String', 'STRING', 'string',
seq(choice('String', 'STRING', 'string'), '[', $.number, ']'),
'WString', 'WSTRING', 'wstring',
seq(choice('WString', 'WSTRING', 'wstring'), '[', $.number, ']'),
'Char', 'CHAR', 'char',
'WChar', 'WCHAR', 'wchar',
seq('ARRAY', '[', $.number, '..', $.number, ']', 'OF', $.type),
$.identifier,
$.string
),
statement: $ => choice(
$.assignment,
$.if_statement,
$.case_statement,
$.for_statement,
$.while_statement,
$.repeat_statement,
$.region_statement,
$.function_call,
$.exit_statement,
$.continue_statement,
$.empty_statement,
$.var_declaration,
$.var_temp_declaration,
$.var_constant_declaration,
$.var_retain_declaration,
$.var_non_retain_declaration
),
empty_statement: $ => ';',
assignment: $ => seq(
field('target', $.lvalue),
':=',
field('value', $.expression),
';'
),
lvalue: $ => choice(
$.identifier,
$.prefixed_identifier,
$.field_access,
$.array_access,
$.function_name
),
function_name: $ => $.string,
prefixed_identifier: $ => seq('#', $.identifier),
field_access: $ => seq($.lvalue, '.', choice($.identifier, $.prefixed_identifier)),
array_access: $ => seq($.lvalue, '[', $.expression, ']'),
if_statement: $ => seq(
'IF', $.expression, 'THEN',
repeat($.statement),
repeat($.elsif_clause),
optional($.else_clause),
'END_IF'
),
elsif_clause: $ => seq(
'ELSIF', $.expression, 'THEN', repeat($.statement)
),
else_clause: $ => seq('ELSE', repeat($.statement)),
case_statement: $ => seq(
'CASE', $.expression, 'OF',
repeat($.case_item),
optional($.else_clause),
'END_CASE',
optional(';')
),
case_item: $ => seq(
choice($.number, $.identifier, $.prefixed_identifier, $.range),
':', repeat($.statement)
),
range: $ => seq($.number, '..', $.number),
for_statement: $ => seq(
'FOR', $.identifier, ':=', $.expression, 'TO', $.expression, optional(seq('BY', $.expression)), 'DO',
repeat($.statement),
'END_FOR'
),
while_statement: $ => seq(
'WHILE', $.expression, 'DO',
repeat($.statement),
'END_WHILE'
),
repeat_statement: $ => seq(
'REPEAT',
repeat($.statement),
'UNTIL', $.expression, 'END_REPEAT'
),
region_statement: $ => seq(
'REGION', $.region_name,
repeat($.statement),
'END_REGION',
optional($.region_name)
),
region_name: $ => repeat1(
choice(
$.identifier,
$.string,
'/',
'-'
)
),
function_call: $ => seq(
choice($.identifier, $.string),
'(',
optional(seq(
$.parameter_list,
optional(seq('=>', $.identifier))
)),
')'
),
parameter_list: $ => seq(
$.parameter,
repeat(seq(',', $.parameter))
),
parameter: $ => seq($.identifier, ':=', $.expression),
expression: $ => choice(
$.number,
$.hex_number,
$.time_value,
$.identifier,
$.prefixed_identifier,
$.field_access,
$.array_access,
$.string,
$.function_call,
$.unary_expression,
$.binary_expression,
seq('(', $.expression, ')')
),
unary_expression: $ => prec.right(10, choice(
seq('NOT', $.expression),
seq('+', $.expression),
seq('-', $.expression)
)),
binary_expression: $ => choice(
prec.left(2, seq($.expression, 'OR', $.expression)),
prec.left(3, seq($.expression, 'XOR', $.expression)),
prec.left(4, seq($.expression, 'AND', $.expression)),
prec.left(5, seq($.expression, choice('=', '<>'), $.expression)),
prec.left(6, seq($.expression, choice('<', '>', '<=', '>='), $.expression)),
prec.left(7, seq($.expression, choice('+', '-'), $.expression)),
prec.left(8, seq($.expression, choice('*', '/', 'MOD'), $.expression))
),
number: $ => token(/\d+(\.\d+)?/),
hex_number: $ => token(seq('16#', /[0-9A-Fa-f_]+/)),
time_value: $ => token(seq('T#', /\d+(_?\d)*(ms|s|min|h)/)),
string: $ => choice(
seq("'", repeat(choice(/[^'\n]/, "''")), "'"),
seq('"', repeat(choice(/[^"\n]/, '""')), '"')
),
// Support for STRING type with length: STRING[80]
string_type: $ => seq(
choice('STRING', 'String', 'string', 'WSTRING', 'WString', 'wstring'),
optional(seq('[', $.number, ']'))
),
// Support for SCL character literals: 'A', WCHAR#'B'
char_literal: $ => choice(
seq("'", /[^']/, "'"),
seq('WCHAR#', "'", /[^']/, "'")
),
identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
line_comment: $ => token(seq('//', /.*/)),
block_comment: $ => token(seq(
'(*',
repeat(choice(/[^*]/, seq('*', /[^)]/))),
'*)'
)),
c_style_comment: $ => token(seq(
'(/*',
repeat(choice(/[^*]/, seq('*', /[^)]/))),
'*/)'
)),
exit_statement: $ => 'EXIT',
continue_statement: $ => 'CONTINUE'
}
});