# Barebones Dockerfile syntax for JOE.  Doesn't handle more sophisticated sh syntax.

=Idle
=Command	bold
=Comment 	green
=Constant 	cyan
=Ident		cyan
=Escape 	magenta
=Keyword 	bold
=Var		bold cyan
=Bad		bold red

# Start of line is special
:start Idle
	*		idle
	" \t"		start
	"#"		comment				noeat
	"A-Za-z"	command				buffer noeat
	"\n"		start

# Comments between commands
:comment Comment
	*		comment
	"\n"		start

# Comments in the middle of a command
:comment_idle Comment
	*		comment_idle
	"\n"		idle

# Start of line in the middle of "idle" mode (skips command recognition in case a comment
# comes in the middle of a RUN)
:start_idle Idle
	*		idle				noeat
	"#"		comment_idle			recolor=-1

# Generic middle-of-a-command
:idle Idle
	*		idle
	"$"		idle				recolor=-1 call=.variable()
	"\n"		start
	"\\"		escape				recolor=-1

:escape Escape
	*		idle				recolor=-2 noeat
	"\\\""		idle
	"\r"		escape
	"\n"		start_idle

:command Idle
	*		idle				noeat istrings
	"FROM"		from
	"MAINTAINER"	string_command
	"RUN"		list_command
	"CMD"		list_command
	"SHELL"		list_command
	"LABEL"		label
	"EXPOSE"	generic_command
	"ENV"		generic_command
	"ADD"		list_command
	"COPY"		list_command
	"ENTRYPOINT"	list_command
	"VOLUME"	list_command
	"USER"		string_command
	"WORKDIR"	string_command
	"ARG"		generic_command
	"ONBUILD"	generic_command
	"STOPSIGNAL"	generic_command
	"HEALTHCHECK"	healthcheck_command
done
	"a-zA-Z"	command

# EXPOSE, ENV, ARG, ONBUILD, STOPSIGNAL
:generic_command Command
	*		idle

# MAINTAINER, USER, WORKDIR
:string_command Command
	*		string_command_data

:string_command_data Ident
	*		string_command_data
	"$"		string_command_data		recolor=-1 call=.variable()
	"\n"		start

# FROM
:from Command
	*		from_image			noeat

:from_image Ident
	*		from_image
	":@"		from_tag			noeat
	"\n"		start

:from_tag Idle
	*		from_tag
	"\n"		start

# RUN, CMD, ADD, COPY, ENTRYPOINT, VOLUME
:list_command Command
	*		idle				noeat
	" \t"		list_command
	"["		array				noeat
	"\n"		start

:array Idle
	*		array
	"[]"		bracket				noeat
	"\""		array				recolor=-1 call=.string() save_c
	"'"		array_bad			noeat
	"\n"		start

:array_bad Bad
	*		array

:comma Idle
	*		array				noeat

:bracket Escape
	"]"		idle
	"["		array

# LABEL
:label Command
	*		label_key
	"\n"		start

:label_key Ident
	*		label_key
	"="		label_value			noeat
	"\n"		start

:label_value Idle
	*		label_value
	"\""		label_value			recolor=-1 call=.string() save_c
	"\n"		start

# HEALTHCHECK
:healthcheck_command Command
       *               idle                            noeat
       "a-zA-Z"        healthcheck_argument            buffer noeat
       "\-"            healthcheck_option              recolor=-1
       " \t"           healthcheck_command
       "\\"            healthcheck_escape              recolor=-1

:healthcheck_argument Idle
       *               idle                            noeat istrings
       "NONE"          healthcheck_none
       "CMD"           list_command
done
       "a-zA-Z"        healthcheck_argument

:healthcheck_option Ident
       *               healthcheck_option
       "\n"            start
       "\\"            healthcheck_escape              recolor=-1
       " \t"           healthcheck_command
       "="             healthcheck_value

:healthcheck_value Idle
       *               healthcheck_value
       "\n"            start
       "\\"            healthcheck_escape
       " \t"           healthcheck_command

:healthcheck_escape Escape
       "\r"            healthcheck_escape
       *               healthcheck_command

:healthcheck_none Command
       *               idle                            noeat

.subr variable

:variable Var
	*		variable			recolor=-2 return noeat
	"A-Za-z_"	variable_name
	"{"		variable_long

:variable_name Var
	*		variable_name			return noeat
	"A-Za-z0-9_"	variable_name

:variable_long Var
	*		variable_long
	&		variable			return noeat
	"\n"		variable			return noeat
	"}"		variable			return
	":"		variable_after

:variable_after Idle
	*		variable_after
	&		variable_after			return noeat
	"}"		variable_long			noeat

.end

.subr string

:string Constant
	*		string
	&		string				return
	"\n"		string				return noeat
	"\\"		string_escape			recolor=-1
	"$"		string				recolor=-1 call=.variable()

:string_escape Escape
	*		string

.end
