383 lines
9.1 KiB
JavaScript
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'
|
|
|
|
}
|
|
});
|