Skip to content

Commit

Permalink
[asciidoctor-java] ensure ifeval can use build time attributes properly
Browse files Browse the repository at this point in the history
  • Loading branch information
rmannibucau committed Dec 11, 2023
1 parent 2157ebe commit 17eca02
Showing 1 changed file with 27 additions and 15 deletions.
42 changes: 27 additions & 15 deletions asciidoc-java/src/main/java/io/yupiik/asciidoc/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ private Collection<Element> parseLine(final Reader reader, final String line,
doParse(new Reader(readIfBlock(reader)), l -> true, resolver, currentAttributes),
macro.options()));
case "ifeval" -> elements.add(new ConditionalBlock(
new ConditionalBlock.Ifeval(parseCondition(macro.label().strip())),
new ConditionalBlock.Ifeval(parseCondition(macro.label().strip(), currentAttributes)),
doParse(new Reader(readIfBlock(reader)), l -> true, resolver, currentAttributes),
macro.options()));
default -> elements.add(macro);
Expand Down Expand Up @@ -672,7 +672,7 @@ private Collection<Element> parseLine(final Reader reader, final String line,
return flattenTexts(elements);
}

private Predicate<ConditionalBlock.Context> parseCondition(final String condition) {
private Predicate<ConditionalBlock.Context> parseCondition(final String condition, final Map<String, String> attributeAtParsingTime) {
final int sep1 = condition.indexOf(' ');
if (sep1 < 0) {
throw new IllegalArgumentException("Unknown expression: '" + condition + "'");
Expand All @@ -685,25 +685,37 @@ private Predicate<ConditionalBlock.Context> parseCondition(final String conditio
final var leftOperand = stripQuotes(condition.substring(0, sep1).strip());
final var operator = condition.substring(sep1 + 1, sep2).strip();
final var rightOperand = stripQuotes(condition.substring(sep2).strip());
final var parsingAttributes = !attributeAtParsingTime.isEmpty() ?
new HashMap<>(attributeAtParsingTime) :
Map.<String, String>of();
final Function<ConditionalBlock.Context, Function<String, String>> attributeAccessor = ctx ->
// ensure levels and implicit attributes are well evaluated
key -> parsingAttributes.getOrDefault(key, ctx.attribute(key));
return switch (operator) {
case "==" -> context -> eval(leftOperand, rightOperand, context, Objects::equals);
case "!=" -> context -> !eval(leftOperand, rightOperand, context, Objects::equals);
case "==" -> context -> eval(leftOperand, rightOperand, attributeAccessor.apply(context), Objects::equals);
case "!=" -> context -> !eval(leftOperand, rightOperand, attributeAccessor.apply(context), Objects::equals);
case "<" -> context -> evalNumbers(leftOperand, rightOperand, context, (a, b) -> a < b);
case "<=" -> context ->
Double.parseDouble(earlyAttributeReplacement(leftOperand, context::attribute)) <=
Double.parseDouble(earlyAttributeReplacement(rightOperand, context::attribute));
case ">" -> context ->
Double.parseDouble(earlyAttributeReplacement(leftOperand, context::attribute)) >
Double.parseDouble(earlyAttributeReplacement(rightOperand, context::attribute));
case ">=" -> context ->
Double.parseDouble(earlyAttributeReplacement(leftOperand, context::attribute)) >=
Double.parseDouble(earlyAttributeReplacement(rightOperand, context::attribute));
case "<=" -> context -> {
final var attributes = attributeAccessor.apply(context);
return Double.parseDouble(earlyAttributeReplacement(leftOperand, attributes)) <=
Double.parseDouble(earlyAttributeReplacement(rightOperand, attributes));
};
case ">" -> context -> {
final var attributes = attributeAccessor.apply(context);
return Double.parseDouble(earlyAttributeReplacement(leftOperand, attributes)) >
Double.parseDouble(earlyAttributeReplacement(rightOperand, attributes));
};
case ">=" -> context -> {
final var attributes = attributeAccessor.apply(context);
return Double.parseDouble(earlyAttributeReplacement(leftOperand, attributes)) >=
Double.parseDouble(earlyAttributeReplacement(rightOperand, attributes));
};
default -> throw new IllegalArgumentException("Unknown operator '" + operator + "'");
};
}

private boolean eval(final String leftOperand, final String rightOperand, final ConditionalBlock.Context context, final BiPredicate<String, String> test) {
return test.test(earlyAttributeReplacement(leftOperand, context::attribute), earlyAttributeReplacement(rightOperand, context::attribute));
private boolean eval(final String leftOperand, final String rightOperand, final Function<String, String> context, final BiPredicate<String, String> test) {
return test.test(earlyAttributeReplacement(leftOperand, context), earlyAttributeReplacement(rightOperand, context));
}

private boolean evalNumbers(final String leftOperand, final String rightOperand, final ConditionalBlock.Context context, final BiPredicate<Double, Double> test) {
Expand Down

0 comments on commit 17eca02

Please sign in to comment.