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, $.type_definition, $.data_block, $.statement ) ), type_definition: $ => seq( alias('TYPE', $.keyword), field('name', choice($.string, $.identifier)), optional(seq(alias('TITLE', $.keyword), '=', choice($.string, $.identifier))), optional($.attribute_list), optional(seq(alias('VERSION', $.keyword), ':', $.number)), alias('STRUCT', $.keyword), repeat(choice($.var_item, $.block_comment)), alias('END_STRUCT', $.keyword), alias('END_TYPE', $.keyword) ), data_block: $ => seq( alias('DATA_BLOCK', $.keyword), field('name', choice($.string, $.identifier)), optional(seq(alias('TITLE', $.keyword), '=', choice($.string, $.identifier))), optional($.attribute_list), optional(seq(alias('VERSION', $.keyword), ':', $.number)), optional(choice( alias('NON_RETAIN', $.keyword), alias('RETAIN', $.keyword) )), optional($.var_declaration), alias('BEGIN', $.keyword), repeat($.statement), alias('END_DATA_BLOCK', $.keyword) ), 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) } });