Files
tia-lsp/grammar.js
T
lazar 553192e7a1 feat: improve tree-sitter syntax highlighting with named nodes
- Add aliases for keywords (VAR_INPUT, BEGIN, IF, etc.) as named nodes
- Add aliases for built-in types (INT, BOOL, STRING, etc.) as type_builtin
- Add aliases for constants (TRUE, FALSE)
- Add aliases for operators (AND, OR, NOT, MOD)
- Add prefix node for # variable prefix highlighting
- Highlight FB instance names in calls
- Highlight FB parameter names as properties
- Update README with detailed syntax highlighting features
- Update .gitignore for *.wasm files
2026-02-20 23:29:26 +01:00

460 lines
13 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],
[$.fb_call, $.function_call],
[$.fb_parameter, $.parameter],
[$.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(
alias('ORGANIZATION_BLOCK', $.keyword),
field('name', $.string),
optional(seq(alias('TITLE', $.keyword), '=', choice($.string, $.identifier))),
optional($.attribute_list),
optional(seq(alias('VERSION', $.keyword), ':', $.number)),
repeat($.var_declaration),
alias('BEGIN', $.keyword),
repeat($.statement),
alias('END_ORGANIZATION_BLOCK', $.keyword)
),
function_block: $ => seq(
alias('FUNCTION_BLOCK', $.keyword),
field('name', $.string),
optional(seq(alias('TITLE', $.keyword), '=', choice($.string, $.identifier))),
optional($.attribute_list),
optional(seq(alias('VERSION', $.keyword), ':', $.number)),
repeat($.var_declaration),
alias('BEGIN', $.keyword),
repeat($.statement),
alias('END_FUNCTION_BLOCK', $.keyword)
),
function: $ => seq(
alias('FUNCTION', $.keyword),
field('name', $.string),
optional(seq(':', $.type)),
optional(seq(alias('TITLE', $.keyword), '=', choice($.string, $.identifier))),
optional($.attribute_list),
optional(seq(alias('VERSION', $.keyword), ':', $.number)),
repeat(choice(
$.var_declaration,
$.var_temp_declaration,
$.var_constant_declaration,
$.var_retain_declaration,
$.var_non_retain_declaration,
$.block_comment
)),
alias('BEGIN', $.keyword),
repeat($.statement),
alias('END_FUNCTION', $.keyword)
),
attribute_list: $ => seq(
'{',
$.attribute,
repeat(seq(choice(',', ';'), $.attribute)),
optional(choice(',', ';')),
'}'
),
attribute: $ => seq(
$.identifier,
':=',
$.attribute_value
),
attribute_value: $ => choice(
$.string,
alias('TRUE', $.constant),
alias('FALSE', $.constant),
$.identifier
),
var_declaration: $ => seq(
choice(
alias('VAR_INPUT', $.keyword),
alias('VAR_OUTPUT', $.keyword),
alias('VAR_IN_OUT', $.keyword),
alias('VAR', $.keyword),
alias('VAR_TEMP', $.keyword),
seq(alias('VAR', $.keyword), alias('CONSTANT', $.keyword)),
seq(alias('VAR', $.keyword), alias('RETAIN', $.keyword)),
seq(alias('VAR', $.keyword), alias('NON_RETAIN', $.keyword)),
alias('VAR_CONSTANT', $.keyword),
alias('VAR_RETAIN', $.keyword),
alias('VAR_NON_RETAIN', $.keyword)
),
repeat(choice($.var_item, $.block_comment)),
alias('END_VAR', $.keyword)
),
var_temp_declaration: $ => seq(
alias('VAR_TEMP', $.keyword),
repeat(choice($.var_item, $.block_comment)),
alias('END_VAR', $.keyword)
),
var_constant_declaration: $ => seq(
choice(
seq(alias('VAR', $.keyword), alias('CONSTANT', $.keyword)),
alias('VAR_CONSTANT', $.keyword)
),
repeat(choice($.var_item, $.block_comment)),
alias('END_VAR', $.keyword)
),
var_retain_declaration: $ => seq(
choice(
seq(alias('VAR', $.keyword), alias('RETAIN', $.keyword)),
alias('VAR_RETAIN', $.keyword)
),
repeat(choice($.var_item, $.block_comment)),
alias('END_VAR', $.keyword)
),
var_non_retain_declaration: $ => seq(
choice(
seq(alias('VAR', $.keyword), alias('NON_RETAIN', $.keyword)),
alias('VAR_NON_RETAIN', $.keyword)
),
repeat(choice($.var_item, $.block_comment)),
alias('END_VAR', $.keyword)
),
var_item: $ => seq(
field('name', $.identifier),
optional($.attribute_list),
':',
field('type', $.type),
optional(seq(':=', $.expression)),
';'
),
type: $ => choice(
alias('BOOL', $.type_builtin),
alias('Bool', $.type_builtin),
alias('INT', $.type_builtin),
alias('Int', $.type_builtin),
alias('int', $.type_builtin),
alias('DINT', $.type_builtin),
alias('DInt', $.type_builtin),
alias('dint', $.type_builtin),
alias('SINT', $.type_builtin),
alias('SInt', $.type_builtin),
alias('sint', $.type_builtin),
alias('USINT', $.type_builtin),
alias('USInt', $.type_builtin),
alias('usint', $.type_builtin),
alias('UINT', $.type_builtin),
alias('UInt', $.type_builtin),
alias('uint', $.type_builtin),
alias('WORD', $.type_builtin),
alias('Word', $.type_builtin),
alias('word', $.type_builtin),
alias('DWORD', $.type_builtin),
alias('DWord', $.type_builtin),
alias('dword', $.type_builtin),
alias('BYTE', $.type_builtin),
alias('Byte', $.type_builtin),
alias('byte', $.type_builtin),
alias('REAL', $.type_builtin),
alias('Real', $.type_builtin),
alias('real', $.type_builtin),
alias('LREAL', $.type_builtin),
alias('LReal', $.type_builtin),
alias('lreal', $.type_builtin),
alias('TIME', $.type_builtin),
alias('Time', $.type_builtin),
alias('time', $.type_builtin),
alias('TOD', $.type_builtin),
alias('Tod', $.type_builtin),
alias('tod', $.type_builtin),
alias('DT', $.type_builtin),
alias('Dt', $.type_builtin),
alias('dt', $.type_builtin),
alias('String', $.type_builtin),
alias('STRING', $.type_builtin),
alias('string', $.type_builtin),
seq(choice(alias('String', $.type_builtin), alias('STRING', $.type_builtin), alias('string', $.type_builtin)), '[', $.number, ']'),
alias('WString', $.type_builtin),
alias('WSTRING', $.type_builtin),
alias('wstring', $.type_builtin),
seq(choice(alias('WString', $.type_builtin), alias('WSTRING', $.type_builtin), alias('wstring', $.type_builtin)), '[', $.number, ']'),
alias('Char', $.type_builtin),
alias('CHAR', $.type_builtin),
alias('char', $.type_builtin),
alias('WChar', $.type_builtin),
alias('WCHAR', $.type_builtin),
alias('wchar', $.type_builtin),
seq(alias('ARRAY', $.keyword), '[', $.number, '..', $.number, ']', alias('OF', $.keyword), $.type),
$.identifier,
$.string
),
statement: $ => choice(
$.assignment,
$.fb_call,
$.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: $ => ';',
fb_call: $ => seq(
field('instance', choice($.prefixed_identifier, $.identifier)),
'(',
optional(seq(
$.fb_parameter_list
)),
')'
),
fb_parameter_list: $ => seq(
$.fb_parameter,
repeat(seq(',', $.fb_parameter))
),
fb_parameter: $ => seq(
field('name', $.identifier),
choice(':=', '=>'),
$.expression
),
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(alias('#', $.prefix), $.identifier),
field_access: $ => seq($.lvalue, '.', choice($.identifier, $.prefixed_identifier)),
array_access: $ => seq($.lvalue, '[', $.expression, ']'),
if_statement: $ => seq(
alias('IF', $.keyword),
$.expression,
alias('THEN', $.keyword),
repeat($.statement),
repeat($.elsif_clause),
optional($.else_clause),
alias('END_IF', $.keyword)
),
elsif_clause: $ => seq(
alias('ELSIF', $.keyword),
$.expression,
alias('THEN', $.keyword),
repeat($.statement)
),
else_clause: $ => seq(alias('ELSE', $.keyword), repeat($.statement)),
case_statement: $ => seq(
alias('CASE', $.keyword),
$.expression,
alias('OF', $.keyword),
repeat($.case_item),
optional($.else_clause),
alias('END_CASE', $.keyword),
optional(';')
),
case_item: $ => seq(
choice($.number, $.identifier, $.prefixed_identifier, $.range),
':',
repeat($.statement)
),
range: $ => seq($.number, '..', $.number),
for_statement: $ => seq(
alias('FOR', $.keyword),
$.identifier,
':=',
$.expression,
alias('TO', $.keyword),
$.expression,
optional(seq(alias('BY', $.keyword), $.expression)),
alias('DO', $.keyword),
repeat($.statement),
alias('END_FOR', $.keyword)
),
while_statement: $ => seq(
alias('WHILE', $.keyword),
$.expression,
alias('DO', $.keyword),
repeat($.statement),
alias('END_WHILE', $.keyword)
),
repeat_statement: $ => seq(
alias('REPEAT', $.keyword),
repeat($.statement),
alias('UNTIL', $.keyword),
$.expression,
alias('END_REPEAT', $.keyword)
),
region_statement: $ => seq(
alias('REGION', $.keyword),
$.region_name,
repeat($.statement),
alias('END_REGION', $.keyword),
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(alias('NOT', $.operator), $.expression),
seq('+', $.expression),
seq('-', $.expression)
)),
binary_expression: $ => choice(
prec.left(2, seq($.expression, alias('OR', $.operator), $.expression)),
prec.left(3, seq($.expression, alias('XOR', $.operator), $.expression)),
prec.left(4, seq($.expression, alias('AND', $.operator), $.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('*', '/', alias('MOD', $.operator)), $.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]/, '""')), '"')
),
string_type: $ => seq(
choice(alias('STRING', $.type_builtin), alias('String', $.type_builtin), alias('string', $.type_builtin), alias('WSTRING', $.type_builtin), alias('WString', $.type_builtin), alias('wstring', $.type_builtin)),
optional(seq('[', $.number, ']'))
),
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: $ => alias('EXIT', $.keyword),
continue_statement: $ => alias('CONTINUE', $.keyword)
}
});