liquid fixes to FHIRPath

This commit is contained in:
Grahame Grieve 2022-12-20 23:09:44 +13:00
parent e2dff7d118
commit 2f0a45046e
3 changed files with 87 additions and 4 deletions

View File

@ -76,6 +76,7 @@ public class FHIRLexer {
private SourceLocation currentStartLocation;
private int id;
private String name;
private boolean liquidMode; // in liquid mode, || terminates the expression and hands the parser back to the host
public FHIRLexer(String source, String name) throws FHIRLexerException {
this.source = source == null ? "" : source;
@ -272,6 +273,12 @@ public class FHIRLexer {
throw error("Unterminated string");
cursor++;
current = "`"+source.substring(currentStart+1, cursor-1)+"`";
} else if (ch == '|' && liquidMode) {
cursor++;
ch = source.charAt(cursor);
if (ch == '|')
cursor++;
current = source.substring(currentStart, cursor);
} else if (ch == '@'){
int start = cursor;
cursor++;
@ -520,5 +527,11 @@ public class FHIRLexer {
public String getSource() {
return source;
}
public boolean isLiquidMode() {
return liquidMode;
}
public void setLiquidMode(boolean liquidMode) {
this.liquidMode = liquidMode;
}
}

View File

@ -262,6 +262,7 @@ public class FHIRPathEngine {
private String location; // for error messages
private boolean allowPolymorphicNames;
private boolean doImplicitStringConversion;
private boolean liquidMode; // in liquid mode, || terminates the expression and hands the parser back to the host
// if the fhir path expressions are allowed to use constants beyond those defined in the specification
// the application can implement them by providing a constant resolver
@ -6034,5 +6035,13 @@ public class FHIRPathEngine {
this.allowPolymorphicNames = allowPolymorphicNames;
}
public boolean isLiquidMode() {
return liquidMode;
}
public void setLiquidMode(boolean liquidMode) {
this.liquidMode = liquidMode;
}
}

View File

@ -93,6 +93,7 @@ public class LiquidEngine implements IEvaluationContext {
this.externalHostServices = hostServices;
engine = new FHIRPathEngine(context);
engine.setHostServices(this);
engine.setLiquidMode(true);
}
public ILiquidEngineIncludeResolver getIncludeResolver() {
@ -152,22 +153,82 @@ public class LiquidEngine implements IEvaluationContext {
}
}
private enum LiquidFilter {
PREPEND;
public static LiquidFilter fromCode(String code) {
if ("prepend".equals(code)) {
return PREPEND;
}
return null;
}
}
private class LiquidExpressionNode {
private LiquidFilter filter; // null at root
private ExpressionNode expression; // null for some filters
public LiquidExpressionNode(LiquidFilter filter, ExpressionNode expression) {
super();
this.filter = filter;
this.expression = expression;
}
}
private class LiquidStatement extends LiquidNode {
private String statement;
private ExpressionNode compiled;
private List<LiquidExpressionNode> compiled = new ArrayList<>();
@Override
public void evaluate(StringBuilder b, Base resource, LiquidEngineContext ctxt) throws FHIRException {
if (compiled == null)
compiled = engine.parse(statement);
List<Base> items = engine.evaluate(ctxt, resource, resource, resource, compiled);
if (compiled.size() == 0) {
FHIRLexer lexer = new FHIRLexer(statement, "liquid statement");
lexer.setLiquidMode(true);
compiled.add(new LiquidExpressionNode(null, engine.parse(lexer)));
while (!lexer.done()) {
if (lexer.getCurrent().equals("||")) {
lexer.next();
String f = lexer.getCurrent();
LiquidFilter filter = LiquidFilter.fromCode(f);
if (filter == null) {
lexer.error("Unknown Liquid filter '"+f+"'");
}
lexer.next();
if (!lexer.done() && lexer.getCurrent().equals(":")) {
lexer.next();
compiled.add(new LiquidExpressionNode(filter, engine.parse(lexer)));
} else {
compiled.add(new LiquidExpressionNode(filter, null));
}
} else {
lexer.error("Unexpected syntax parsing liquid statement");
}
}
}
String t = null;
for (LiquidExpressionNode i : compiled) {
if (i.filter == null) { // first
t = stmtToString(ctxt, engine.evaluate(ctxt, resource, resource, resource, i.expression));
} else switch (i.filter) {
case PREPEND:
t = stmtToString(ctxt, engine.evaluate(ctxt, resource, resource, resource, i.expression)) + t;
break;
}
}
b.append(t);
}
private String stmtToString(LiquidEngineContext ctxt, List<Base> items) {
StringBuilder b = new StringBuilder();
boolean first = true;
for (Base i : items) {
if (first) first = false; else b.append(", ");
String s = renderingSupport != null ? renderingSupport.renderForLiquid(ctxt.externalContext, i) : null;
b.append(s != null ? s : engine.convertToString(i));
}
return b.toString();
}
}