const IDENTIFIER = /[a-zA-Z0-9_]+/;
const QUALIFIED_IDENTIFIER =
/([a-zA-Z0-9_]+\:)?([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+/;
const PROMISE_GUARD = /[a-zA-Z_]+:/;
const CLASS_EXPRESSION = /[.|&!()a-zA-Z0-9_:][\t .|&!()a-zA-Z0-9_:]*/;
const SQUOTE = /\'((\\(.|\n))|[^'\\])*\'/;
const DQUOTE = /\"((\\(.|\n))|[^"\\])*\"/;
const BQUOTE = /`[^`]*`/;
const QUOTED_STRING = RegExp(
'(' + SQUOTE.source + ')|(' + DQUOTE.source + ')|(' + BQUOTE.source + ')',
);
const CLASS_GUARD = RegExp(
'((' + QUOTED_STRING.source + ')|(' + CLASS_EXPRESSION.source + '))::',
);
module.exports = grammar({
name: 'cfengine',
extras: ($) => [$.comment, $.macro, /[\s]+/],
word: ($) => $.identifier,
rules: {
source_file: ($) => repeat($._block),
_block: ($) => choice($.bundle_block, $.body_block, $.promise_block),
body_block: ($) =>
seq(
$.body_block_keyword,
alias($.identifier, $.body_block_type),
alias($.identifier, $.body_block_name),
optional($.parameter_list),
$.body_block_body,
),
body_block_keyword: (_) => 'body',
body_block_body: ($) =>
seq(
'{',
repeat($._body_attribute),
repeat($.class_guarded_body_attributes),
'}',
),
_body_attribute: ($) => seq($.attribute, ';'),
promise_block: ($) =>
seq(
$.promise_block_keyword,
alias($.identifier, $.promise_block_type),
alias($.identifier, $.promise_block_name),
$.promise_block_body,
),
promise_block_keyword: (_) => 'promise',
promise_block_body: ($) =>
seq(
'{',
repeat($._body_attribute),
repeat(
alias(
$.class_guarded_body_attributes,
$.class_guarded_promise_block_attributes,
),
),
'}',
),
class_guarded_body_attributes: ($) =>
seq($.class_guard, repeat($._body_attribute)),
bundle_block: ($) =>
seq(
$.bundle_block_keyword,
alias($.identifier, $.bundle_block_type),
alias($.identifier, $.bundle_block_name),
optional($.parameter_list),
$.bundle_block_body,
),
bundle_block_keyword: (_) => 'bundle',
parameter_list: ($) =>
seq(
'(',
optional(
seq(
repeat(seq(alias($.identifier, $.parameter), ',')),
alias($.identifier, $.parameter),
optional(','),
),
),
')',
),
bundle_block_body: ($) => seq('{', repeat($.bundle_section), '}'),
bundle_section: ($) =>
seq(
$.promise_guard,
optional($._promises),
repeat($.class_guarded_promises),
),
_bare_value: ($) =>
choice(
$.quoted_string,
$.qualified_identifier,
$.list,
$.call,
$.dollar_expression,
$.at_expression,
),
_right_value: ($) =>
choice($._bare_value, seq($._bare_value, $._bare_value)),
_variable_reference: ($) =>
choice($.qualified_identifier, $.indexed_identifier),
dollar_expression: ($) =>
choice(
seq(
alias('$', $.dollar_expression_operator),
alias('(', $.dollar_expression_start),
choice(
alias($._variable_reference, $.dollar_expression_reference),
$.dollar_expression,
),
alias(')', $.dollar_expression_end),
),
seq(
alias('$', $.dollar_expression_operator),
alias('{', $.dollar_expression_start),
choice(
alias($._variable_reference, $.dollar_expression_reference),
$.dollar_expression,
),
alias('}', $.dollar_expression_end),
),
),
at_expression: ($) =>
choice(
seq(
alias('@', $.at_expression_operator),
alias('(', $.at_expression_start),
alias($._variable_reference, $.at_expression_reference),
alias(')', $.at_expression_end),
),
seq(
alias('@', $.at_expression_operator),
alias('{', $.at_expression_start),
alias($._variable_reference, $.at_expression_reference),
alias('}', $.at_expression_end),
),
),
calling_identifier: ($) =>
choice(
$.qualified_identifier,
$.dollar_expression,
),
call: ($) => seq($.calling_identifier, '(', optional($._value_list), ')'),
list: ($) => seq('{', optional($._value_list), '}'),
_value_list: ($) =>
seq($._right_value, repeat(seq(',', $._right_value)), optional(',')),
_double_value: ($) => seq($._right_value, $._right_value),
class_guarded_promises: ($) => seq($.class_guard, optional($._promises)),
_promises: ($) => seq($.promise, repeat(choice($.promise, $.half_promise))),
promise: ($) =>
seq(
alias($.quoted_string, $.promiser),
optional(seq('->', alias($._right_value, $.stakeholder))),
optional($.attribute),
repeat(seq(',', $.attribute)),
optional(','),
';',
),
half_promise: ($) =>
seq($.attribute, repeat(seq(',', $.attribute)), optional(','), ';'),
attribute: ($) =>
seq(alias($.identifier, $.attribute_name), '=>', $._right_value),
index: ($) =>
seq(
'[',
choice(
$.identifier,
$.dollar_expression
),
']'
),
indexed_identifier: ($) =>
seq(
$.qualified_identifier,
$.index,
repeat($.index)
),
quoted_string: ($) => QUOTED_STRING,
identifier: ($) => IDENTIFIER,
qualified_identifier: ($) => QUALIFIED_IDENTIFIER,
promise_guard: ($) => PROMISE_GUARD,
class_guard: ($) => CLASS_GUARD,
comment: ($) => token(seq('#', /.*/)),
macro: ($) => token(/@(if |else|endif)[^\n]*/),
},
});