class Asciidoctor::PreprocessorReader
def preprocess_conditional_directive keyword, target, delimiter, text
ifndef directives, and for the conditional expression for the ifeval directive.
Used for a single-line conditional block in the case of the ifdef or
text - The text associated with this directive (occurring between the square brackets)
can be defined or undefined.
attributes must be defined or undefined, ',' means any of the attributes
delimiter - The conditional delimiter for multiple attributes ('+' means all
used in the condition (blank in the case of the ifeval directive)
target - The target, which is the name of one or more attributes that are
keyword - The conditional inclusion directive (ifdef, ifndef, ifeval, endif)
found.
preprocessing recursively until the next line of available content is
not skipping, mark whether the condition is satisfied and continue
open and close delimiters of any nested conditional blocks. If Reader is
the cursor. If Reader is currently skipping content, then simply track the
Preprocess the conditional directive (ifdef, ifndef, ifeval, endif) under
Internal: Preprocess the directive to conditionally include or exclude content.
def preprocess_conditional_directive keyword, target, delimiter, text # attributes are case insensitive target = target.downcase unless (no_target = target.empty?) if keyword == 'endif' if text logger.error message_with_context %(malformed preprocessor directive - text not permitted: endif::#{target}[#{text}]), source_location: cursor elsif @conditional_stack.empty? logger.error message_with_context %(unmatched preprocessor directive: endif::#{target}[]), source_location: cursor elsif no_target || target == (pair = @conditional_stack[-1])[:target] @conditional_stack.pop @skipping = @conditional_stack.empty? ? false : @conditional_stack[-1][:skipping] else logger.error message_with_context %(mismatched preprocessor directive: endif::#{target}[], expected endif::#{pair[:target]}[]), source_location: cursor end return true elsif @skipping skip = false else # QUESTION any way to wrap ifdef & ifndef logic up together? case keyword when 'ifdef' if no_target logger.error message_with_context %(malformed preprocessor directive - missing target: ifdef::[#{text}]), source_location: cursor return true end case delimiter when ',' # skip if no attribute is defined skip = target.split(',', -1).none? {|name| @document.attributes.key? name } when '+' # skip if any attribute is undefined skip = target.split('+', -1).any? {|name| !@document.attributes.key? name } else # if the attribute is undefined, then skip skip = !@document.attributes.key?(target) end when 'ifndef' if no_target logger.error message_with_context %(malformed preprocessor directive - missing target: ifndef::[#{text}]), source_location: cursor return true end case delimiter when ',' # skip if any attribute is defined skip = target.split(',', -1).any? {|name| @document.attributes.key? name } when '+' # skip if all attributes are defined skip = target.split('+', -1).all? {|name| @document.attributes.key? name } else # if the attribute is defined, then skip skip = @document.attributes.key?(target) end when 'ifeval' if no_target # the text in brackets must match a conditional expression if text && EvalExpressionRx =~ text.strip # NOTE assignments must happen before call to resolve_expr_val for compatibility with Opal lhs = $1 # regex enforces a restricted set of math-related operations (==, !=, <=, >=, <, >) op = $2 rhs = $3 skip = ((resolve_expr_val lhs).send op, (resolve_expr_val rhs)) ? false : true rescue true else logger.error message_with_context %(malformed preprocessor directive - #{text ? 'invalid expression' : 'missing expression'}: ifeval::[#{text}]), source_location: cursor return true end else logger.error message_with_context %(malformed preprocessor directive - target not permitted: ifeval::#{target}[#{text}]), source_location: cursor return true end end end # conditional inclusion block if keyword == 'ifeval' || !text @skipping = true if skip @conditional_stack << { target: target, skip: skip, skipping: @skipping } # single line conditional inclusion else unless @skipping || skip replace_next_line text.rstrip # HACK push dummy line to stand in for the opening conditional directive that's subsequently dropped unshift '' # NOTE force line to be processed again if it looks like an include directive # QUESTION should we just call preprocess_include_directive here? @look_ahead -= 1 if text.start_with? 'include::' end end true end