// (C) Copyright 2019 Hewlett Packard Enterprise Development LP
// partially derived from the TOML example:
// https://github.com/pest-parser/pest/blob/master/grammars/src/grammars/toml.pest
dockerfile = { SOI ~ meta_step ~ (NEWLINE ~ meta_step)* ~ EOI }
meta_step = _{ ws* ~ (step | comment)? ~ ws* }
step = _{
(
from |
run |
arg |
label |
copy |
entrypoint |
cmd |
env |
// todos:
// add | workdir | user
// things that we probably won't bother supporting
// expose | volume | onbuild | stopsignal | healthcheck | shell
// deprecated: maintainer
// catchall for unsupported directives
misc
)
}
comment = _{ "#" ~ (!NEWLINE ~ ANY)* }
string = @{ "\"" ~ inner ~ "\"" }
inner = @{ (!("\"" | "\\" | "\u{0000}" | "\u{001F}") ~ ANY)* ~ (escape ~ inner)? }
escape = @{ "\\" ~ ("b" | "t" | "n" | "f" | "r" | "\"" | "\\" | unicode | NEWLINE)? }
unicode = @{ "u" ~ ASCII_HEX_DIGIT{4} | "U" ~ ASCII_HEX_DIGIT{8} }
// insignificant whitespace, not repeated
ws = _{ " " | "\t" }
// whitespace that may appear between instruction arguments
// this allows instructions to expand past a newline if escaped
arg_ws = _{ (ws | ("\\" ~ NEWLINE))+ }
arg_ws_maybe = _{ (ws | ("\\" ~ NEWLINE))* }
// continues consuming input beyond a newline, if the newline is preceeded by an
// escape (\)
any_breakable = _{
(
!NEWLINE ~
!("\\" ~ NEWLINE) ~
ANY
)* ~ ("\\" ~ NEWLINE ~ any_breakable)?
}
// consumes any character until the end of the line
any_eol = _{ (!NEWLINE ~ ANY)* }
// consumes all characters until the next whitespace
any_whitespace = _{ (!(NEWLINE | EOI | arg_ws) ~ ANY)+ }
// consumes identifier characters until the next whitespace
identifier_whitespace = _{ (!ws ~ (ASCII_ALPHANUMERIC | "_" | "-"))+ }
// consumes until whitespace or = (for key in key=value pairs)
any_equals = _{ (!(NEWLINE | ws | "=") ~ ANY)+ }
// parses ["foo", "bar", "baz"] with excessive escaping
string_array = _{
(
"[" ~
arg_ws_maybe ~ string ~
(arg_ws_maybe ~ "," ~ arg_ws_maybe ~ string)* ~
","? ~
arg_ws_maybe ~ "]"
) | "[" ~ arg_ws_maybe ~ "]"
}
from_image = @{ (ASCII_ALPHANUMERIC | "_" | "-" | "." | ":" | "/" | "$" | "{" | "}")+ }
from_alias = { identifier_whitespace }
from_alias_outer = _{ arg_ws ~ ^"as" ~ arg_ws ~ from_alias }
from = { ^"from" ~ arg_ws ~ from_image ~ from_alias_outer? }
arg_name = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
arg_value = ${ any_whitespace }
arg_quoted_value = ${ string }
arg = { ^"arg" ~ arg_ws ~ arg_name ~ ("=" ~ (arg_quoted_value | arg_value))? }
label_name = ${ any_equals }
label_quoted_name = ${ string }
label_value = ${ any_whitespace }
label_quoted_value = ${ string }
label_pair = {
(label_quoted_name | label_name) ~ "=" ~ (label_quoted_value | label_value)
}
label = { ^"label" ~ (arg_ws ~ label_pair)+ }
run_shell = @{ any_breakable }
run_exec = { string_array }
run = _{ ^"run" ~ arg_ws ~ (run_exec | run_shell) }
entrypoint_shell = @{ any_breakable }
entrypoint_exec = { string_array }
entrypoint = _{ ^"entrypoint" ~ arg_ws ~ (entrypoint_exec | entrypoint_shell) }
cmd_shell = @{ any_breakable }
cmd_exec = { string_array }
cmd = _{ ^"cmd" ~ arg_ws ~ (cmd_exec | cmd_shell) }
copy_flag_name = @{ ASCII_ALPHA+ }
copy_flag_value = @{ any_whitespace }
copy_flag = { "--" ~ copy_flag_name ~ "=" ~ copy_flag_value }
copy_pathspec = @{ any_whitespace }
copy = { ^"copy" ~ (arg_ws ~ copy_flag)* ~ (arg_ws ~ copy_pathspec){2,} }
env_pair_name = ${ (ASCII_ALPHANUMERIC | "_")+ }
env_pair_value = ${ any_whitespace }
env_pair_quoted_value = ${ string }
env_pair = @{ env_pair_name ~ "=" ~ (env_pair_quoted_value | env_pair_value) }
env_pairs = { (arg_ws ~ env_pair)+ }
env_single_name = @{ ASCII_ALPHA+ }
env_single_value = @{ any_breakable }
env_single = { arg_ws ~ env_single_name ~ env_single_value }
env = _{ ^"env" ~ (env_pairs | env_single) }
misc_instruction = @{ ASCII_ALPHA+ }
misc_arguments = @{ any_breakable }
misc = { misc_instruction ~ misc_arguments }