const PREC = {
conditional: -1,
parenthesized_expression: 1,
parenthesized_list_splat: 1,
string: -1,
under_var: 1,
or: 10,
and: 11,
not: 12,
compare: 13,
bitwise_or: 14,
bitwise_and: 15,
xor: 16,
shift: 17,
plus: 18,
times: 19,
unary: 20,
power: 21,
call: 22,
};
const SPECIAL_CHARACTERS = [
"'",
'"',
'{', '}',
'\\[', '\\]', '$',
'&',
'\\',
'\\s',
'\\n',
'#',
];
module.exports = grammar({
name: 'lammps',
extras: $ => [
$.comment,
/\s/, $._line_continuation,
],
supertypes: $ => [$._statement],
inline: $ => [$._primary_expression, $._concat_expression, $._statement, $._terminator],
externals: $ => [$._concat, $._eof],
word: $ => $._identifier,
rules: {
input_script: $ =>
optional($._statements),
_statements: $ =>
prec(1,
repeat1(seq(
$._statement,
$._terminator,
)),
),
_statement: $ =>
choice(
$._definition,
$.shell,
$.command,),
_definition: $ => choice(
$.fix,
$.compute,
$.variable_def,
$.variable_del,
),
command: $ => seq($.command_name, optional($.args_under)),
fix: $ => seq('fix',
field('fix_id', $.fix_id),
field('group', $.group_id),
field('style', $.fix_style),
field('arguments', optional($.args_under))),
compute: $ => seq('compute',
field('compute_id', $.compute_id),
field('group', $.group_id),
field('style', $.compute_style),
field('arguments', optional($.args_under))),
variable_def: $ => choice(
$._variable_equal,
$._variable_atom,
$._variable_vector,
$._variable_other),
_variable_equal: $ => seq('variable',
field("name", $.variable),
field('style', alias('equal', $.variable_style)),
field("rhs", choice($.expression, $.quoted_expression))),
_variable_atom: $ => seq('variable',
field("name", $.variable),
field('style', alias('atom', $.variable_style)),
field("rhs", choice($.expression, $.quoted_expression))),
_variable_vector: $ => seq('variable',
field("name", $.variable),
field('style', alias('vector', $.variable_style)),
field("rhs", choice($.expression, $.quoted_expression, $.vector_expression))),
_string_like: $ => choice($.concatenation, $.string, $.raw_string, $.triple_string, $.word, $.var_curly, $.var_round,
$.simple_expansion),
variable_style: _ => choice('atomfile', 'file', 'format', 'getenv', 'index',
'internal', 'loop', 'python', 'string', 'timer', 'uloop', 'universe', 'world',
),
_variable_other: $ => seq('variable',
field("name", $.variable),
field('style', $.variable_style),
field("args", repeat1($._string_like))),
variable_del: $ => seq('variable',
field("name", $.variable),
field('style', alias('delete', $.variable_style))),
shell: $ => seq('shell', field('shell_cmd', $._any_string)),
parens: $ => prec(PREC.parenthesized_expression, seq('(', $.expression, ')')),
func: $ => prec(PREC.call,
seq(field("function", $.identifier,),
field("args", $.argument_list))),
argument_list: $ => seq("(",
commaSep1($.expression),
optional(","),
")"),
unary_op: $ => prec(PREC.unary, seq(choice('-', '!'), $.expression)),
binary_op: $ => {
const table = [
[prec.left, '+', PREC.plus],
[prec.left, '-', PREC.plus],
[prec.left, '*', PREC.times],
[prec.left, '/', PREC.times],
[prec.left, '%', PREC.times],
[prec.right, '^', PREC.power],
[prec.left, '==', PREC.compare],
[prec.left, '!=', PREC.compare],
[prec.left, '<', PREC.compare],
[prec.left, '<=', PREC.compare],
[prec.left, '>', PREC.compare],
[prec.left, '>=', PREC.compare],
[prec.left, '&&', PREC.bitwise_and],
[prec.left, '||', PREC.bitwise_or],
[prec.left, '|^', PREC.bitwise_or],
];
return choice(...table.map(([fn, operator, precedence]) => fn(precedence, seq(
field('left', $.expression),
field('operator', operator),
field('right', $.expression),
))));
},
expression: $ => choice(
$.binary_op,
$._expr_ident,
$.int,
$.float,
$.bool,
$.unary_op,
$.func,
$.constant,
$.simple_expansion,
$.thermo_kwarg,
$.atom_property,
$.var_curly,
$.var_round,
$.parens,
$.indexing,
),
quoted_expression: $ => seq('"', $.expression, '"'),
vector_expression: $ => seq(optional('"'), '[', commaSep1($.expression), ']', optional('"')),
fix_id: $ => $.identifier,
compute_id: $ => $.identifier,
group_id: $ => $.identifier,
fix_style: $ => $._style_name, compute_style: $ => $._style_name, args_under: $ => repeat1(field("arg", $._arg_under)),
_terminator: $ => token(prec(PREC.under_var, '\n',)),
_arg_under: $ => choice($._primary_expression,
$.concatenation, $.raw_string),
thermo_kwarg: _ => token(
choice('step', 'elapsed', 'elaplong', 'dt', 'time',
'cpu', 'tpcpu', 'spcpu', 'cpuremain', 'part', 'timeremain',
'atoms', 'temp', 'press', 'pe', 'ke', 'etotal',
'evdwl', 'ecoul', 'epair', 'ebond', 'eangle', 'edihed', 'eimp',
'emol', 'elong', 'etail',
'enthalpy', 'ecouple', 'econserve',
'vol', 'density', 'lx', 'ly', 'lz', 'xlo', 'xhi', 'ylo', 'yhi', 'zlo', 'zhi',
'xy', 'xz', 'yz', 'xlat', 'ylat', 'zlat',
'bonds', 'angles', 'dihedrals', 'impropers',
'pxx', 'pyy', 'pzz', 'pxy', 'pxz', 'pyz',
'fmax', 'fnorm', 'nbuild', 'ndanger',
'cella', 'cellb', 'cellc', 'cellalpha', 'cellbeta', 'cellgamma',)),
atom_property: _ => token(
choice('d', 'mass', 'type', 'mol', 'radius', 'q', 'x', 'y', 'z', 'vx', 'vy', 'vz', 'fx', 'fy', 'fz')
),
constant: _ => token(choice('PI', 'version')),
var_curly: $ => seq('${', $.variable, '}'),
var_round: $ => seq('$(', $.expression, ')'),
simple_expansion: $ => seq('\$', $._concat, alias(/[a-zA-Z0-9]/, $.variable)),
underscore_ident: $ => choice(
$._variable_under,
$._fix_under,
$._compute_under,
),
_expr_ident: $ =>
choice($.underscore_ident, $.identifier,),
indexed_ident: $ => prec(PREC.call,
seq(
field("ident", choice($.underscore_ident, $.indexed_ident)),
"[",
choice($.int, $.glob),
"]",
)
),
_v_prefix: _ => token(prec(PREC.under_var, 'v_')),
_f_prefix: _ => token(prec(PREC.under_var, 'f_')),
_c_prefix: _ => token(prec(PREC.under_var, 'c_')),
_variable_under: $ => seq($._v_prefix, $._concat, $.variable,),
_fix_under: $ => seq($._f_prefix, $._concat, $.fix_id,),
_compute_under: $ => seq($._c_prefix, $._concat, $.compute_id,),
indexing: $ => prec(PREC.call,
seq(
field("value", $.expression),
'[',
field("index", choice($.int, $.glob, $.expression)),
']')),
int: _ => token(/[0-9]+/),
float: _ => {
const digits = repeat1(/[0-9]+_?/);
const exponent = seq(/[eE][\+-]?/, digits);
return token(seq(
choice(
seq(digits, '.', optional(digits), optional(exponent)),
seq(optional(digits), '.', digits, optional(exponent)),
seq(digits, exponent),
),
optional(choice(/[Ll]/, /[jJ]/)), ));
},
_primary_expression: $ => choice(
$.word,
$.underscore_ident,
$.indexed_ident,
$.string,
$.triple_string,
$.int,
$.float,
$.bool,
$.var_curly,
$.var_round,
$.simple_expansion,
),
_concat_expression: $ => choice($.word, $.string, $.var_curly, $.var_round, $.simple_expansion),
concatenation: $ => prec(-1, seq(
choice(
$._concat_expression,
),
repeat1(seq(
choice($._concat,
),
choice(
$._concat_expression,
),
)),
optional(seq($._concat, '$')),
)),
string: $ => seq(
'"',
repeat(seq(
choice(
seq(optional('$'), $.string_content),
$.var_curly,
$.var_round,
$.simple_expansion,
$.raw_string,
),
)),
optional('$'),
'"',
),
_line_continuation: _ => /&\s*\n/,
raw_string: $ => seq("'",
repeat(
seq(
alias(/[^'\n]+/, $.string_content),
optional($._line_continuation)
)
),
"'"),
triple_string: $ => seq(
'"""',
repeat(seq(
choice(
seq(optional('$'),
alias($._triple_string_content, $.string_content)),
$.var_curly,
$.var_round,
$.simple_expansion,
alias(/"([^"])+"/, $.sub_string_content),
alias(/'([^'])+'/, $.sub_string_content),
),
)),
optional('$'),
'"""',
),
_triple_string_content: _ => token(prec(-1, /([^$"'\\\r\n]|\\(.|\r?\n))+/)),
string_content: _ => token(prec(-1, /([^"'$\\\r\n]|\\(.|\r?\n))+/)),
_any_string: $ => token(prec(-1, /[^\n\&]+/)), command_name: $ => $._name,
_name: _ => /[a-z0-9A-Z_]+/,
_style_name: _ => /[a-z0-9A-Z_\/]+/,
variable: $ => $._identifier,
_identifier: _ => /[_a-z0-9A-Z]+/,
identifier: $ => $._identifier,
glob: _ => '*',
comment: _ => token(prec(-10, /#.*/)),
bool: $ => choice($.true, $.false),
true: _ => token(choice('true', 'yes', 'on')),
false: _ => token(choice('false', 'no', 'off')),
word: $ => token(
repeat1(choice(
noneOf(...SPECIAL_CHARACTERS),
seq('\\', noneOf('\\s'))
))
),
}
});
function noneOf(...characters) {
const negatedString = characters.map(c => c == '\\' ? '\\\\' : c).join('')
return new RegExp('[^' + negatedString + ']')
}
function commaSep1(rule) {
return sep1(rule, ',');
}
function sep1(rule, separator) {
return seq(rule, repeat(seq(separator, rule)));
}