joe/syntax/jsf.jsf
2024-10-16 10:58:52 +08:00

628 lines
16 KiB
Plaintext

# JOE Syntax-Highlighting Description
# for
# JOE Syntax-Highlighting Descriptions
#
# Author: Charles J. Tabony
# Date: 2007-2-13
#
# This is a highlighting description for files like this one.
#
# When CHECKING is defined, it is very aggressive about error checking. The
# idea is that anywhere the highlighted file contains a syntax error, at least
# one visible character should be highlighted as Bad. While that feature is
# useful for finding syntax errors, it is annoying when editing a file, since
# nearly everything is an error until you finish typing it.
#
# In order to not annoy people by default, but keep the option of strictly
# checking syntax, I predicated the stricter checking on the CHECKING parameter.
# By default, things that are incomplete are generally not marked as errors.
# Only things that appear to be actual mistakes are highlighted as Bad. To
# enable the stricter checking, one can highlight the file with the jsf_check
# syntax. jsf_check.jsf simply calls the entire jsf.jsf file with CHECKING
# defined.
#
# The idea is for authors of a jsf file to edit their file, highlight it with
# jsf_check, and then look for any red characters. That way they can check for
# syntax errors before testing the changes.
#####################
# Color Definitions #
#####################
=Idle
=Comment
=Conditional +Precond +Preproc
=Parameter +Ident
=Keyword
=Color +Type
=ColorRef
=State +Ident
=Subr +Ident
=Constant
=Number +Constant
=String +Constant
=StringEscape +Escape +String
=Bad
##################
# Initial States #
##################
# This is a dummy state that simply jumps to comment_or_bad. It is here so that
# when this file calls itself with the STRINGS parameter defined, comment_or_bad
# will effectively be the initial state. comment_or_bad should be the initial
# state because strings and istrings options can only be used as the last option
# of a transition.
.ifdef STRINGS
:strings_initial Idle
* comment_or_bad noeat
.endif
# Each new line (that is not considered bad from the beginning) begins in the
# idle state. The first non-whitespace character determines what the rest of
# the line should contain. Following a strings or istrings option, only strings
# and comments are allowed until the word "done" denotes the end of the list.
:idle Idle
* bad noeat
" \t\n" idle
.ifdef STRINGS
.else
"-" sync_lines_first
"." conditional_first mark recolor=-1
"=" color_definition_first
":" state_first
"*&%" special_character recolor=-1
.endif
"\"" string recolor=-1
.ifdef STRINGS
"\i" special_word mark recolor=-1 buffer
.endif
"#" comment recolor=-1
##############
# Sync Lines #
##############
# Following a '-' should be either the number of sync lines or nothing (meaning
# unlimited). Nothing else other than a comment should appear on the same line.
.ifdef STRINGS
# A sync lines directive should not appear between "[i]strings" and "done".
.else
# If we see a non-digit or a '0', then we have seen the entire sync lines
# directive. The only thing that may appear on the rest of the line is a
# comment. Otherwise there may be more digits in the number.
:sync_lines_first Number
* comment_or_bad noeat
"0" comment_or_bad
"1-9" sync_lines
# Highlight the remainder of the number.
:sync_lines Number
* comment_or_bad noeat
"0-9" sync_lines
.endif
##########################
# Conditional Directives #
##########################
# Following a '.' should be a conditional directive.
.ifdef STRINGS
# A conditional directive should not appear between "[i]strings" and "done".
.else
# Start buffering the conditional directive.
:conditional_first Conditional
* conditional noeat buffer
# Recognize the set of conditional directives.
:conditional Idle
* conditional_unknown noeat strings
"ifdef" ifdef_color
"else" conditional_color
"endif" conditional_color
"subr" subr_color
"end" conditional_color
done
"\c" conditional
# We encountered what looks like a conditional directive but is unrecognized as
# such.
:conditional_unknown Idle
.ifdef CHECKING
* bad_line recolormark noeat
.else
* comment_or_bad noeat
.endif
# We saw a conditional directive that does not take an argument. Nothing else
# other than a comment should appear on the same line.
:conditional_color Conditional
* comment_or_bad noeat
# We saw a ".ifdef" which must be followed by a parameter.
:ifdef_color Conditional
* need_parameter noeat
# We loop over whitespace until we see the first character of the parameter.
:need_parameter Idle
* bad noeat
" \t" need_parameter
"\i" parameter recolor=-1
# Now we highlight the remainder of the parameter.
:parameter Parameter
* comment_or_bad noeat
"\c" parameter
# The following three states are identical to the previous three except the
# color.
:subr_color Conditional
* need_subr noeat
:need_subr Idle
* bad noeat
" \t" need_subr
"\i" subr recolor=-1
:subr Subr
* comment_or_bad noeat
"\c" subr
.endif
####################
# Color Definition #
####################
# Following an '=' should be a color definition.
.ifdef STRINGS
# Color definitions should not appear between "[i]strings" and "done".
.else
# A color name must have at least one character.
:color_definition_first Color
* color_definition
" \t#\n" bad noeat
# Highlight any remaining characters until we see whitespace, a comment, or a
# newline.
:color_definition Color
* color_definition
" \t#\n" colors_ws noeat
# The color name may be followed by zero or more standard colors or attributes,
# ending in a comment or newline.
:colors_ws Idle
* color_bad recolor=-1
" \t" colors_ws
"+" color_ref recolor=-1
"#\n" comment noeat
:color_ref ColorRef
* colors_ws noeat
"\c" color_ref
# We have encountered something that is not recognized as a standard color or
# attribute. Continue to highlight characters as Bad until we see whitespace, a
# comment, or a newline.
:color_bad Bad
* color_bad
" \t#\n" colors_ws noeat
.endif
#########
# State #
#########
# Following a ':' should be a state definition.
.ifdef STRINGS
# New states should not appear between "[i]strings" and "done".
.else
# A state name must begin with an alpha character or an underscore.
:state_first State
* bad noeat
"\i" state
# Subsequent characters in a state name must be alpha-numeric or underscores.
:state State
* bad noeat
"\c" state
" \t" need_state_color recolor=-1
# A state must have a color.
:need_state_color Idle
* state_color recolor=-1
" \t" need_state_color
"#\n" bad noeat
# Highlight any remaining characters until we see whitespace, a comment, or a
# newline.
:state_color Color
* state_color
" \t" context_ws recolor=-1
"#\n" comment_or_bad noeat
# Following the state color, there might be one or more contexts. Loop over
# whitespace until we find something else.
:context_ws Idle
* comment_or_bad noeat
" \t" context_ws
"\i" context mark recolor=-1 buffer
# Here we recognize the possible contexts.
:context Idle
* context_unknown noeat strings
"comment" context_color
"string" context_color
done
"\c" context
# We encountered what looks like a context but is unrecognized as such.
:context_unknown Idle
.ifdef CHECKING
* context_bad recolormark noeat
.else
* context_ws noeat
.endif
# We encountered a valid context.
:context_color Keyword
* context_ws noeat
# We saw something that is not a valid context name with checking enabled.
# Continue to highlight it as Bad until we see whitespace or a comment.
:context_bad Bad
* context_bad
" \t#\n" context_ws noeat
.endif
##############
# Transition #
##############
# A state transition starts with a '*', an '&', or a string.
.ifdef STRINGS
# Transitions must start with a string between "[i]strings" and "done".
.else
# We saw either a '*' or an '&'. Now we need the next state.
:special_character Keyword
* need_next_state noeat
.endif
# We are in a string. Continue until we see the close quote or a newline.
# Highlight escaped characters within the string differently. They start with a
# '\'.
:string String string
* string
"\\" escape recolor=-1
"\"" need_next_state
.ifdef CHECKING
"\n" bad
.else
"\n" bad noeat
.endif
# Highlight an escaped character within a string.
:escape StringEscape string
* string
# Loop over whitespace until we see the first character of the next state.
:need_next_state Idle
* bad noeat
" \t" need_next_state
"\i" next_state recolor=-1
# Now we highlight the remainder of the next state.
:next_state State
* bad noeat
"\c" next_state
" \t" options_ws
"#\n" comment noeat
# Following the next state should be zero or more options. Loop over whitespace
# until we find an option, comment, or newline.
:options_ws Idle
* option_bad recolor=-1
" \t" options_ws
"\i" option mark recolor=-1 buffer
"#\n" comment noeat
# Here we recognize the possible options. The strings and istrings options
# cannot be used between "[i]strings" and "done". Since conditional directives
# cannot be used between "[i]strings" and "done" either, the list must be
# duplicated, once without and once with the strings and istrings options.
:option Idle
.ifdef STRINGS
* option_unknown recolormark noeat strings
"noeat" option_color
"recolor" recolor_color
"mark" option_color
"markend" option_color
"recolormark" option_color
"buffer" option_color
"save_c" option_color
"save_s" option_color
"hold" option_color
"call" call_color
"return" option_color
"reset" option_color
done
.else
* option_unknown recolormark noeat strings
"noeat" option_color
"recolor" recolor_color
"mark" option_color
"markend" option_color
"recolormark" option_color
"buffer" option_color
"save_c" option_color
"save_s" option_color
"strings" strings_color
"istrings" strings_color
"hold" option_color
"call" call_color
"return" option_color
"reset" option_color
done
.endif
"\c" option
# We encountered what looks like an option but is unrecognized as such.
:option_unknown Idle
.ifdef CHECKING
* option_bad recolormark noeat
.else
* options_ws noeat
.endif
# We have encountered an option that does not take an argument. Highlight it
# and continue to look for more options.
:option_color Keyword
* options_ws noeat
.ifdef STRINGS
# The strings and istrings options cannot be used between "[i]strings" and
# "done".
.else
# The strings and istrings options are followed by a list of transitions.
# Rather than duplicate all of the states that highlight transitions, we call
# this entire file as a subroutine and use the STRINGS parameter to disable
# everything else and enable the done keyword. We return to the comment_or_bad
# state since we will return after seeing the done keyword, and nothing but a
# comment should follow the done keyword.
:strings_color Keyword
* comment_or_bad noeat call=jsf(STRINGS)
.endif
# Highlight the recolor option.
:recolor_color Keyword
* recolor_equal noeat
# The recolor option must be followed by an '='. Loop over whitespace until we
# find one.
:recolor_equal Idle
.ifdef CHECKING
* option_bad recolormark noeat
.else
* options_ws noeat
.endif
" \t" recolor_equal
"=" recolor_minus mark
# The recolor option takes an integer argument, and that integer must be
# negative. Thus the '=' must be followed by a minus sign. Loop over
# whitespace until we find one.
:recolor_minus Idle
.ifdef CHECKING
* option_bad recolormark noeat
.else
* options_ws noeat
.endif
" \t" recolor_minus
"-" recolor_amount_first mark recolor=-1
# The first digit of the argument to recolor must be non-zero.
:recolor_amount_first Number
.ifdef CHECKING
* option_bad recolormark noeat
.else
* options_ws recolormark noeat
"0" option_bad recolormark noeat
.endif
"1-9" recolor_amount
# Keep highlighting digits until we see something else.
:recolor_amount Number
* option_bad recolormark recolor=-1
"0-9" recolor_amount
" \t#\n" options_ws noeat
# Highlight the call option.
:call_color Keyword
* call_equal noeat
# The call option must be followed by an '='. Loop over whitespace until we
# find one.
:call_equal Idle
.ifdef CHECKING
* option_bad recolormark noeat
.else
* options_ws noeat
.endif
" \t" call_equal
"=" call_file_or_dot mark
# The first part of the argument to the call option is the name of the file
# containing the subroutine or a '.', implying the current file. Loop over
# whitespace until we see one of those two things.
:call_file_or_dot Idle
.ifdef CHECKING
* option_bad recolormark noeat
.else
* options_ws noeat
.endif
" \t" call_file_or_dot
"\i" call_file mark recolor=-1
"." call_dot mark
# Highlight the remainder of the file name. The file name can be followed by a
# '.', which must then be followed by the name of a subroutine, or by a list of
# parameters in parentheses. The '.', if present, cannot have whitespace on
# either side.
:call_file Subr
.ifdef CHECKING
* option_bad recolormark noeat
.else
* options_ws noeat
.endif
"\c" call_file
"." call_dot mark recolor=-1
" \t(" call_open_paren noeat
# We saw a '.'. The next character must start the name of a subroutine.
:call_dot Idle
.ifdef CHECKING
* option_bad recolormark noeat
.else
* options_ws noeat
.endif
"(" call_dot_bad recolormark noeat
"\i" call_subr mark recolor=-1
# We have seen a dot followed by an open parenthesis. A dot must be followed by
# a subroutine name. Highlight the dot as Bad.
:call_dot_bad Bad
* call_open_paren noeat
# Highlight the remainder of the subroutine name. Following the subroutine name
# must be a list of parameters in parentheses, possibly preceded by whitespace.
:call_subr Subr
.ifdef CHECKING
* option_bad recolormark noeat
.else
* options_ws noeat
.endif
"\c" call_subr
" \t(" call_open_paren noeat
# Loop over whitespace until we find the open parenthesis.
:call_open_paren Idle
.ifdef CHECKING
* option_bad recolormark noeat
.else
* options_ws noeat
.endif
" \t" call_open_paren
"(" call_parameters_ws
# The list of parameters is delimited by whitespace. Loop over whitespace until
# we find either the beginning of a parameter or a close parenthesis. We should
# not see a comment or newline since the list should be terminated by a close
# parenthesis.
:call_parameters_ws Idle
* call_parameter_bad recolor=-1
" \t" call_parameters_ws
"-" call_parameter_undef
"\i" call_parameter recolor=-1
")" options_ws
"#\n" bad noeat
# We saw a "-". The next character should start the parameter being undefined.
:call_parameter_undef Parameter
* call_parameters_ws noeat
"\i" call_parameter recolor=-2
# Highlight the remainder of the parameter.
:call_parameter Parameter
* call_parameters_ws noeat
"\c" call_parameter
# We saw something that is not a valid parameter name. Continue to highlight it
# as Bad until we see whitespace.
:call_parameter_bad Bad
* call_parameter_bad
") \t#\n" call_parameters_ws noeat
# We saw something that is not a valid option name. Continue to highlight it as
# Bad until we see whitespace or a comment.
:option_bad Bad
* option_bad
" \t#\n" options_ws noeat
########
# Done #
########
.ifdef STRINGS
# The special word, "done", can only be used after a strings or istrings option.
# Recognize the done keyword.
:special_word Idle
* bad_line recolormark noeat strings
"done" done_color
done
"\c" special_word
# Highlight the done keyword and return to highlighting things normally, since
# the list of strings has been terminated.
:done_color Keyword
* comment_or_bad return noeat
.endif
##################
# Comment or Bad #
##################
# We have seen everything that should appear on the current line except an
# optional comment. Loop over whitespace until we find a comment or newline.
:comment_or_bad Idle
* bad noeat
" \t" comment_or_bad
"#\n" comment noeat
###########
# Comment #
###########
# Continue to highlight the comment until the end of the line.
:comment Comment comment
* comment
"BFHNTX" comment noeat call=comment_todo.comment_todo()
"\n" idle
#######
# Bad #
#######
.ifdef CHECKING
# We have encountered incorrect syntax. Loop over whitespace until we see the
# first visible character. Highlight that character and the rest of the line as
# Bad.
:bad Bad
* bad_line
" \t\n" bad
.else
# When not performing strict checking, don't go searching for the next visible
# character to highlight as Bad. Simply highlight the rest of the line as Bad,
# even if it is invisible.
:bad Bad
* bad_line noeat
.endif
# Continue to highlight everything as Bad until the end of the line.
:bad_line Bad
* bad_line
"\n" idle