diff --git a/grammar.js b/grammar.js index a8c1610..9d072d5 100644 --- a/grammar.js +++ b/grammar.js @@ -1,364 +1,332 @@ module.exports = grammar({ - name: 'dockerfile', + name: "dockerfile", - extras: $ => [ - $.comment, - /\s+/, - '\\\n' - ], + extras: ($) => [$.comment, /\s+/, "\\\n"], - rules: { - source_file: $ => repeat(seq($._instruction, "\n")), + rules: { + source_file: ($) => repeat(seq($._instruction, "\n")), - _instruction: $ => choice( - $.from_instruction, - $.run_instruction, - $.cmd_instruction, - $.label_instruction, - $.expose_instruction, - $.env_instruction, - $.add_instruction, - $.copy_instruction, - $.entrypoint_instruction, - $.volume_instruction, - $.user_instruction, - $.workdir_instruction, - $.arg_instruction, - $.onbuild_instruction, - $.stopsignal_instruction, - $.healthcheck_instruction, - $.shell_instruction, - $.maintainer_instruction, - $.cross_build_instruction, - ), + _instruction: ($) => + choice( + $.from_instruction, + $.run_instruction, + $.cmd_instruction, + $.label_instruction, + $.expose_instruction, + $.env_instruction, + $.add_instruction, + $.copy_instruction, + $.entrypoint_instruction, + $.volume_instruction, + $.user_instruction, + $.workdir_instruction, + $.arg_instruction, + $.onbuild_instruction, + $.stopsignal_instruction, + $.healthcheck_instruction, + $.shell_instruction, + $.maintainer_instruction, + $.cross_build_instruction + ), - from_instruction: $ => seq( - alias(/[fF][rR][oO][mM]/, "FROM"), - $._non_newline_whitespace, - optional($.param), - $.image_spec, - optional(seq( - alias(/[aA][sS]/, "AS"), - field("as", $.image_alias), - )), - ), + from_instruction: ($) => + seq( + alias(/[fF][rR][oO][mM]/, "FROM"), + $._non_newline_whitespace, + optional($.param), + $.image_spec, + optional(seq(alias(/[aA][sS]/, "AS"), field("as", $.image_alias))) + ), - run_instruction: $ => seq( - alias(/[rR][uU][nN]/, "RUN"), - $._non_newline_whitespace, - choice( - $.string_array, - $.shell_command, - ), - ), + run_instruction: ($) => + seq( + alias(/[rR][uU][nN]/, "RUN"), + $._non_newline_whitespace, + choice($.string_array, $.shell_command) + ), - cmd_instruction: $ => seq( - alias(/[cC][mM][dD]/, "CMD"), - $._non_newline_whitespace, - choice( - $.string_array, - $.shell_command, - ), - ), + cmd_instruction: ($) => + seq( + alias(/[cC][mM][dD]/, "CMD"), + $._non_newline_whitespace, + choice($.string_array, $.shell_command) + ), - label_instruction: $ => seq( - alias(/[lL][aA][bB][eE][lL]/, "LABEL"), - $._non_newline_whitespace, - repeat1($.label_pair), - ), + label_instruction: ($) => + seq( + alias(/[lL][aA][bB][eE][lL]/, "LABEL"), + $._non_newline_whitespace, + repeat1($.label_pair) + ), - expose_instruction: $ => seq( - alias(/[eE][xX][pP][oO][sS][eE]/, "EXPOSE"), - repeat1(choice($.expose_port, $.expansion)), - ), + expose_instruction: ($) => + seq( + alias(/[eE][xX][pP][oO][sS][eE]/, "EXPOSE"), + repeat1(choice($.expose_port, $.expansion)) + ), - env_instruction: $ => seq( - alias(/[eE][nN][vV]/, "ENV"), - $._non_newline_whitespace, - choice( - repeat1($.env_pair), - alias($._spaced_env_pair, $.env_pair), - ) - ), + env_instruction: ($) => + seq( + alias(/[eE][nN][vV]/, "ENV"), + $._non_newline_whitespace, + choice(repeat1($.env_pair), alias($._spaced_env_pair, $.env_pair)) + ), - add_instruction: $ => seq( - alias(/[aA][dD][dD]/, "ADD"), - $._non_newline_whitespace, - optional($.param), - $.path, - $._non_newline_whitespace, - $.path, - ), + add_instruction: ($) => + seq( + alias(/[aA][dD][dD]/, "ADD"), + $._non_newline_whitespace, + optional($.param), + $.path, + $._non_newline_whitespace, + $.path + ), - copy_instruction: $ => seq( - alias(/[cC][oO][pP][yY]/, "COPY"), - $._non_newline_whitespace, - optional($.param), - $.path, - $._non_newline_whitespace, - $.path, - ), + copy_instruction: ($) => + seq( + alias(/[cC][oO][pP][yY]/, "COPY"), + $._non_newline_whitespace, + optional($.param), + $.path, + $._non_newline_whitespace, + $.path + ), - entrypoint_instruction: $ => seq( - alias(/[eE][nN][tT][rR][yY][pP][oO][iI][nN][tT]/, "ENTRYPOINT"), - $._non_newline_whitespace, - choice( - $.string_array, - $.shell_command, - ), - ), + entrypoint_instruction: ($) => + seq( + alias(/[eE][nN][tT][rR][yY][pP][oO][iI][nN][tT]/, "ENTRYPOINT"), + $._non_newline_whitespace, + choice($.string_array, $.shell_command) + ), - volume_instruction: $ => seq( - alias(/[vV][oO][lL][uU][mM][eE]/, "VOLUME"), - $._non_newline_whitespace, - choice( - $.string_array, - seq( - $.path, - repeat(seq($._non_newline_whitespace, $.path)), - ), - ), - ), + volume_instruction: ($) => + seq( + alias(/[vV][oO][lL][uU][mM][eE]/, "VOLUME"), + $._non_newline_whitespace, + choice( + $.string_array, + seq($.path, repeat(seq($._non_newline_whitespace, $.path))) + ) + ), - user_instruction: $ => seq( - alias(/[uU][sS][eE][rR]/, "USER"), - $._non_newline_whitespace, - field("user", alias($._user_name_group, $.unquoted_string)), - optional(seq( - token.immediate(":"), - field("group", alias($._user_name_group, $.unquoted_string)), - )), - ), + user_instruction: ($) => + seq( + alias(/[uU][sS][eE][rR]/, "USER"), + $._non_newline_whitespace, + field("user", alias($._user_name_group, $.unquoted_string)), + optional( + seq( + token.immediate(":"), + field("group", alias($._user_name_group, $.unquoted_string)) + ) + ) + ), - _user_name_group: $ => repeat1(choice( - token.immediate(/[a-z][-a-z0-9_]*/), - $.expansion, - )), + _user_name_group: ($) => + repeat1(choice(token.immediate(/[a-z][-a-z0-9_]*/), $.expansion)), - workdir_instruction: $ => seq( - alias(/[wW][oO][rR][kK][dD][iI][rR]/, "WORKDIR"), - $._non_newline_whitespace, - $.path, - ), + workdir_instruction: ($) => + seq( + alias(/[wW][oO][rR][kK][dD][iI][rR]/, "WORKDIR"), + $._non_newline_whitespace, + $.path + ), - arg_instruction: $ => seq( - alias(/[aA][rR][gG]/, "ARG"), - $._non_newline_whitespace, - field("name", alias(/[a-zA-Z0-9_]+/, $.unquoted_string)), - optional(seq( - token.immediate("="), - field("default", choice( - $.double_quoted_string, - $.unquoted_string, - )), - )), - ), + arg_instruction: ($) => + seq( + alias(/[aA][rR][gG]/, "ARG"), + $._non_newline_whitespace, + field("name", alias(/[a-zA-Z0-9_]+/, $.unquoted_string)), + optional( + seq( + token.immediate("="), + field("default", choice($.double_quoted_string, $.unquoted_string)) + ) + ) + ), - onbuild_instruction: $ => seq( - alias(/[oO][nN][bB][uU][iI][lL][dD]/, "ONBUILD"), - $._non_newline_whitespace, - $._instruction, - ), + onbuild_instruction: ($) => + seq( + alias(/[oO][nN][bB][uU][iI][lL][dD]/, "ONBUILD"), + $._non_newline_whitespace, + $._instruction + ), - stopsignal_instruction: $ => seq( - alias(/[sS][tT][oO][pP][sS][iI][gG][nN][aA][lL]/, "STOPSIGNAL"), - $._non_newline_whitespace, - $._stopsignal_value, - ), + stopsignal_instruction: ($) => + seq( + alias(/[sS][tT][oO][pP][sS][iI][gG][nN][aA][lL]/, "STOPSIGNAL"), + $._non_newline_whitespace, + $._stopsignal_value + ), - _stopsignal_value: $ => repeat1(choice( - /[A-Z0-9]+/, - $.expansion, - )), + _stopsignal_value: ($) => repeat1(choice(/[A-Z0-9]+/, $.expansion)), - healthcheck_instruction: $ => seq( - alias(/[hH][eE][aA][lL][tT][hH][cC][hH][eE][cC][kK]/, "HEALTHCHECK"), - $._non_newline_whitespace, - choice( - "NONE", - seq( - repeat($.param), - $.cmd_instruction, - ), - ), - ), + healthcheck_instruction: ($) => + seq( + alias(/[hH][eE][aA][lL][tT][hH][cC][hH][eE][cC][kK]/, "HEALTHCHECK"), + $._non_newline_whitespace, + choice("NONE", seq(repeat($.param), $.cmd_instruction)) + ), - shell_instruction: $ => seq( - alias(/[sS][hH][eE][lL][lL]/, "SHELL"), - $._non_newline_whitespace, - $.string_array, - ), + shell_instruction: ($) => + seq( + alias(/[sS][hH][eE][lL][lL]/, "SHELL"), + $._non_newline_whitespace, + $.string_array + ), - maintainer_instruction: $ => seq( - alias(/[mM][aA][iI][nN][tT][aA][iI][nN][eE][rR]/, "MAINTAINER"), - /.*/, - ), + maintainer_instruction: ($) => + seq( + alias(/[mM][aA][iI][nN][tT][aA][iI][nN][eE][rR]/, "MAINTAINER"), + /.*/ + ), - cross_build_instruction: $ => seq( - alias(/[cC][rR][oO][sS][sS]_[bB][uU][iI][lL][dD][a-zA-Z_]*/, "CROSS_BUILD"), - /.*/, - ), - - path: $ => seq( - choice( - /[^-\s\$]/, // cannot start with a '-' to avoid conflicts with params - $.expansion, - ), - repeat(choice( - /[^\s\$]+/, - $.expansion, - )), - ), + cross_build_instruction: ($) => + seq( + alias( + /[cC][rR][oO][sS][sS]_[bB][uU][iI][lL][dD][a-zA-Z_]*/, + "CROSS_BUILD" + ), + /.*/ + ), - expansion: $ => seq( - '$', - choice( - $.variable, - seq('{', alias(/[^\}]+/, $.variable), '}'), - ) - ), + path: ($) => + seq( + choice( + /[^-\s\$]/, // cannot start with a '-' to avoid conflicts with params + $.expansion + ), + repeat(choice(/[^\s\$]+/, $.expansion)) + ), - variable: $ => /[a-zA-Z][a-zA-Z0-9_]*/, + expansion: ($) => + seq("$", choice($.variable, seq("{", alias(/[^\}]+/, $.variable), "}"))), - env_pair: $ => seq( - field("name", $._env_key), - token.immediate("="), - field("value", choice( - $.double_quoted_string, - $.unquoted_string, - )), - ), + variable: ($) => /[a-zA-Z][a-zA-Z0-9_]*/, - _spaced_env_pair: $ => seq( - field("name", $._env_key), - token.immediate(/\s+/), - field("value", choice( - $.double_quoted_string, - $.unquoted_string, - )), - ), + env_pair: ($) => + seq( + field("name", $._env_key), + token.immediate("="), + field("value", choice($.double_quoted_string, $.unquoted_string)) + ), - _env_key: $ => alias(/[a-zA-Z][a-zA-Z0-9_]*[a-zA-Z0-9]/, $.unquoted_string), + _spaced_env_pair: ($) => + seq( + field("name", $._env_key), + token.immediate(/\s+/), + field("value", choice($.double_quoted_string, $.unquoted_string)) + ), - expose_port: $ => seq( - /\d+/, - optional(choice( - "/tcp", - "/udp", - )), - ), + _env_key: ($) => + alias(/[a-zA-Z][a-zA-Z0-9_]*[a-zA-Z0-9]/, $.unquoted_string), - label_pair: $ => seq( - field("key", alias(/[-a-zA-Z0-9\._]+/, $.unquoted_string)), - token.immediate("="), - field("value", choice( - $.double_quoted_string, - $.unquoted_string, - )), - ), + expose_port: ($) => seq(/\d+/, optional(choice("/tcp", "/udp"))), - image_spec: $ => seq( - field("name", $.image_name), - seq( - field("tag", optional($.image_tag)), - field("digest", optional($.image_digest)), - ), - ), + label_pair: ($) => + seq( + field("key", alias(/[-a-zA-Z0-9\._]+/, $.unquoted_string)), + token.immediate("="), + field("value", choice($.double_quoted_string, $.unquoted_string)) + ), - image_name: $ => repeat1(choice( - token.immediate(/[^@:\s\$]+/), - $.expansion, - )), + image_spec: ($) => + seq( + field("name", $.image_name), + seq( + field("tag", optional($.image_tag)), + field("digest", optional($.image_digest)) + ) + ), - image_tag: $ => seq( - token.immediate(":"), - repeat1(choice( - token.immediate(/[^@\s\$]+/), - $.expansion, - )) - ), + image_name: ($) => + repeat1(choice(token.immediate(/[^@:\s\$]+/), $.expansion)), - image_digest: $ => seq( - token.immediate("@"), - repeat1(choice( - token.immediate(/[a-zA-Z0-9:]+/), - $.expansion, - )), - ), + image_tag: ($) => + seq( + token.immediate(":"), + repeat1(choice(token.immediate(/[^@\s\$]+/), $.expansion)) + ), - param: $ => seq( - "--", - field("name", token.immediate(/[a-z][-a-z]*/)), - token.immediate("="), - field("value", token.immediate(/[^\s]+/)), - ), + image_digest: ($) => + seq( + token.immediate("@"), + repeat1(choice(token.immediate(/[a-zA-Z0-9:]+/), $.expansion)) + ), - image_alias: $ => repeat1(choice( - /[-a-zA-Z0-9_]+/, - $.expansion, - )), + param: ($) => + seq( + "--", + field("name", token.immediate(/[a-z][-a-z]*/)), + token.immediate("="), + field("value", token.immediate(/[^\s]+/)) + ), - string_array: $ => seq( - "[", - optional(seq( - $.double_quoted_string, - repeat(seq(",", $.double_quoted_string)), - )), - "]", - ), + image_alias: ($) => repeat1(choice(/[-a-zA-Z0-9_]+/, $.expansion)), - shell_command: $ => seq( - $.shell_fragment, - repeat(seq( - $.line_continuation, - repeat($._comment_line), - $.shell_fragment, - )), - ), + string_array: ($) => + seq( + "[", + optional( + seq($.double_quoted_string, repeat(seq(",", $.double_quoted_string))) + ), + "]" + ), - shell_fragment: $ => repeat1(choice( - /[^\\\[\n#\s][^\\\n]*/, - /\\[^\n]/, - )), + shell_command: ($) => + seq( + $.shell_fragment, + repeat( + seq($.line_continuation, repeat($._comment_line), $.shell_fragment) + ) + ), - line_continuation: $ => '\\\n', + shell_fragment: ($) => repeat1(choice(/[^\\\[\n#\s][^\\\n]*/, /\\[^\n]/)), - _comment_line: $ => seq( - alias($._anon_comment, $.comment), '\n' - ), + line_continuation: ($) => "\\\n", - _anon_comment: $ => seq('#', /.*/), + _comment_line: ($) => seq(alias($._anon_comment, $.comment), "\n"), - double_quoted_string: $ => seq( - '"', - repeat(choice( - token.immediate(prec(1, /[^"\n\\\$]+/)), - $.escape_sequence, - $.expansion, - )), - '"' - ), + _anon_comment: ($) => seq("#", /.*/), - unquoted_string: $ => repeat1(choice( - token.immediate(/[^\s\n\"\\\$]+/), - token.immediate("\\ "), - $.expansion, - )), + double_quoted_string: ($) => + seq( + '"', + repeat( + choice( + token.immediate(prec(1, /[^"\n\\\$]+/)), + $.escape_sequence, + $.expansion + ) + ), + '"' + ), - escape_sequence: $ => token.immediate(seq( - '\\', - choice( - /[^xuU]/, - /\d{2,3}/, - /x[0-9a-fA-F]{2,}/, - /u[0-9a-fA-F]{4}/, - /U[0-9a-fA-F]{8}/ - ) - )), + unquoted_string: ($) => + repeat1( + choice( + token.immediate(/[^\s\n\"\\\$]+/), + token.immediate("\\ "), + $.expansion + ) + ), - _non_newline_whitespace: $ => /[\t ]+/, + escape_sequence: ($) => + token.immediate( + seq( + "\\", + choice( + /[^xuU]/, + /\d{2,3}/, + /x[0-9a-fA-F]{2,}/, + /u[0-9a-fA-F]{4}/, + /U[0-9a-fA-F]{8}/ + ) + ) + ), - comment: $ => seq("#", /.*/), - } + _non_newline_whitespace: ($) => /[\t ]+/, + + comment: ($) => seq("#", /.*/), + }, }); -