Skip to content

Commit

Permalink
Qute: add a Scope class for scopes
Browse files Browse the repository at this point in the history
This is not necessary after all but it's much cleaner and avoids duplication of
bindings in nested scopes.
  • Loading branch information
FroMage committed Mar 10, 2020
1 parent 7a67cb3 commit decb48d
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static Expression from(String value) {
if (value == null || value.isEmpty()) {
return EMPTY;
}
return Parser.parseExpression(value, Collections.emptyMap(), null);
return Parser.parseExpression(value, Scope.EMPTY, null);
}

public static Expression literal(String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
Expand Down Expand Up @@ -88,7 +87,7 @@ public IfSectionHelper initialize(SectionInitContext context) {
}

@Override
public Map<String, String> initializeBlock(Map<String, String> outerNameTypeInfos, BlockInfo block) {
public Scope initializeBlock(Scope previousScope, BlockInfo block) {
List<Object> params = null;
if (MAIN_BLOCK_NAME.equals(block.getLabel())) {
params = parseParams(new ArrayList<>(block.getParameters().values()), block);
Expand All @@ -101,7 +100,7 @@ public Map<String, String> initializeBlock(Map<String, String> outerNameTypeInfo
}
addExpressions(params, block);
// {#if} never changes the scope
return Collections.emptyMap();
return previousScope;
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import io.quarkus.qute.Results.Result;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -126,7 +124,7 @@ public LoopSectionHelper initialize(SectionInitContext context) {
}

@Override
public Map<String, String> initializeBlock(Map<String, String> outerNameTypeInfos, BlockInfo block) {
public Scope initializeBlock(Scope previousScope, BlockInfo block) {
if (block.getLabel().equals(MAIN_BLOCK_NAME)) {
String iterable = block.getParameters().get(ITERABLE);
if (iterable == null) {
Expand All @@ -136,17 +134,17 @@ public Map<String, String> initializeBlock(Map<String, String> outerNameTypeInfo
String alias = block.getParameters().get(ALIAS);
if (iterableExpr.typeCheckInfo != null) {
alias = alias.equals(Parameter.EMPTY) ? DEFAULT_ALIAS : alias;
Map<String, String> typeInfos = new HashMap<String, String>(outerNameTypeInfos);
typeInfos.put(alias, iterableExpr.typeCheckInfo + HINT);
return typeInfos;
Scope newScope = new Scope(previousScope);
newScope.put(alias, iterableExpr.typeCheckInfo + HINT);
return newScope;
} else {
Map<String, String> typeInfos = new HashMap<String, String>(outerNameTypeInfos);
// Make sure we do not try to validate against the parent context
typeInfos.put(alias, null);
return typeInfos;
Scope newScope = new Scope(previousScope);
newScope.put(alias, null);
return newScope;
}
} else {
return Collections.emptyMap();
return previousScope;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
import java.io.Reader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
Expand Down Expand Up @@ -55,7 +53,7 @@ class Parser implements Function<String, Expression>, ParserHelper {
private final Deque<SectionNode.Builder> sectionStack;
private final Deque<SectionBlock.Builder> sectionBlockStack;
private final Deque<ParametersInfo> paramsStack;
private final Deque<Map<String, String>> typeInfoStack;
private final Deque<Scope> scopeStack;
private int sectionBlockIdx;
private boolean ignoreContent;
private String id;
Expand Down Expand Up @@ -88,8 +86,8 @@ public CompletionStage<ResultNode> resolve(SectionResolutionContext context) {
this.sectionBlockIdx = 0;
this.paramsStack = new ArrayDeque<>();
this.paramsStack.addFirst(ParametersInfo.EMPTY);
this.typeInfoStack = new ArrayDeque<>();
this.typeInfoStack.addFirst(new HashMap<>());
this.scopeStack = new ArrayDeque<>();
this.scopeStack.addFirst(new Scope(null));
this.line = 1;
this.lineCharacter = 1;
}
Expand Down Expand Up @@ -277,16 +275,9 @@ private void flushTag() {
processParams(tag, sectionName, iter);

// Initialize the block
Map<String, String> typeInfos = typeInfoStack.peek();
Map<String, String> result = sectionStack.peek().factory.initializeBlock(typeInfos, block);
if (!result.isEmpty()) {
Map<String, String> newTypeInfos = new HashMap<>();
newTypeInfos.putAll(typeInfos);
newTypeInfos.putAll(result);
typeInfoStack.addFirst(newTypeInfos);
} else {
typeInfoStack.addFirst(typeInfos);
}
Scope currentScope = scopeStack.peek();
Scope newScope = sectionStack.peek().factory.initializeBlock(currentScope, block);
scopeStack.addFirst(newScope);

ignoreContent = false;

Expand All @@ -304,8 +295,8 @@ private void flushTag() {
processParams(tag, SectionHelperFactory.MAIN_BLOCK_NAME, iter);

// Init section block
Map<String, String> typeInfos = typeInfoStack.peek();
Map<String, String> result = factory.initializeBlock(typeInfos, mainBlock);
Scope currentScope = scopeStack.peek();
Scope newScope = factory.initializeBlock(currentScope, mainBlock);
SectionNode.Builder sectionNode = SectionNode
.builder(sectionName, origin())
.setEngine(engine)
Expand All @@ -320,15 +311,7 @@ private void flushTag() {
// Add node to the parent block
sectionBlockStack.peek().addNode(sectionNode.build());
} else {
if (!result.isEmpty()) {
// The section modifies the type info stack
Map<String, String> newTypeInfos = new HashMap<>();
newTypeInfos.putAll(typeInfos);
newTypeInfos.putAll(result);
typeInfoStack.addFirst(newTypeInfos);
} else {
typeInfoStack.addFirst(typeInfos);
}
scopeStack.addFirst(newScope);
sectionStack.addFirst(sectionNode);
}
}
Expand Down Expand Up @@ -359,16 +342,16 @@ private void flushTag() {
}

// Remove the last type info map from the stack
typeInfoStack.pop();
scopeStack.pop();

} else if (content.charAt(0) == Tag.PARAM.command) {

// {@org.acme.Foo foo}
Map<String, String> typeInfos = typeInfoStack.peek();
Scope currentScope = scopeStack.peek();
int spaceIdx = content.indexOf(" ");
String key = content.substring(spaceIdx + 1, content.length());
String value = content.substring(1, spaceIdx);
typeInfos.put(key, Expressions.TYPE_INFO_SEPARATOR + value + Expressions.TYPE_INFO_SEPARATOR);
currentScope.put(key, Expressions.TYPE_INFO_SEPARATOR + value + Expressions.TYPE_INFO_SEPARATOR);

} else {
sectionBlockStack.peek().addNode(new ExpressionNode(apply(content), engine, origin()));
Expand Down Expand Up @@ -567,13 +550,10 @@ enum State {

}

public static Expression parseExpression(String value, Map<String, String> typeInfos, Origin origin) {
public static Expression parseExpression(String value, Scope scope, Origin origin) {
if (value == null || value.isEmpty()) {
return Expression.EMPTY;
}
if (typeInfos == null) {
typeInfos = Collections.emptyMap();
}
String namespace = null;
List<String> parts;
Object literal = Result.NOT_FOUND;
Expand All @@ -595,20 +575,20 @@ public static Expression parseExpression(String value, Map<String, String> typeI
if (namespace != null) {
// inject:foo.name becomes "[$$namespace$$].foo.name"
typeCheckInfo = TYPE_CHECK_NAMESPACE + parts.stream().collect(Collectors.joining("."));
} else if (typeInfos.containsKey(parts.get(0))) {
typeCheckInfo = typeInfos.get(parts.get(0));
} else {
typeCheckInfo = scope.getBindingType(parts.get(0));
if (typeCheckInfo != null) {
for (String part : parts.subList(1, parts.size())) {
if (Expressions.isVirtualMethod(part)) {
List<String> params = new ArrayList<>(Expressions.parseVirtualMethodParams(part));
for (ListIterator<String> iterator = params.listIterator(); iterator.hasNext();) {
String param = iterator.next();
iterator.set(typeInfos.getOrDefault(param, param));
iterator.set(scope.getBindingTypeOrDefault(param, param));
}
typeCheckInfo += "."
+ Expressions.buildVirtualMethodSignature(Expressions.parseVirtualMethodName(part), params);
} else {
typeCheckInfo += "." + typeInfos.getOrDefault(part, part);
typeCheckInfo += "." + scope.getBindingTypeOrDefault(part, part);
}
}
}
Expand Down Expand Up @@ -637,7 +617,7 @@ static boolean isBracket(char character) {

@Override
public Expression apply(String value) {
return parseExpression(value, typeInfoStack.peek(), origin());
return parseExpression(value, scopeStack.peek(), origin());
}

Origin origin() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.quarkus.qute;

import java.util.HashMap;
import java.util.Map;

public class Scope {

public static final Scope EMPTY = new Scope(null) {
@Override
public void put(String binding, String type) {
throw new UnsupportedOperationException("Immutable empty scope");
}
};

private Scope parentScope;
private Map<String, String> bindings;

public Scope(Scope parentScope) {
this.parentScope = parentScope;
}

public void put(String binding, String type) {
if (bindings == null)
bindings = new HashMap<>();
bindings.put(binding, type);
}

public String getBindingType(String binding) {
// we can contain null types to override outer scopes
if (bindings != null
&& bindings.containsKey(binding))
return bindings.get(binding);
return parentScope != null ? parentScope.getBindingType(binding) : null;
}

public String getBindingTypeOrDefault(String binding, String defaultValue) {
String type = getBindingType(binding);
return type != null ? type : defaultValue;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ default boolean treatUnknownSectionsAsBlocks() {
/**
* Initialize a section block.
*
* @return a map of name to type infos
* @return a new scope if this section introduces a new scope, or the outer scope
* @see BlockInfo#addExpression(String, String)
*/
default Map<String, String> initializeBlock(Map<String, String> outerNameTypeInfos, BlockInfo block) {
return Collections.emptyMap();
default Scope initializeBlock(Scope outerScope, BlockInfo block) {
return outerScope;
}

interface ParserDelegate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import static io.quarkus.qute.Futures.evaluateParams;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand Down Expand Up @@ -64,18 +62,18 @@ public SetSectionHelper initialize(SectionInitContext context) {
}

@Override
public Map<String, String> initializeBlock(Map<String, String> outerNameTypeInfos, BlockInfo block) {
public Scope initializeBlock(Scope previousScope, BlockInfo block) {
if (block.getLabel().equals(MAIN_BLOCK_NAME)) {
Map<String, String> typeInfos = new HashMap<String, String>(outerNameTypeInfos);
Scope newScope = new Scope(previousScope);
for (Entry<String, String> entry : block.getParameters().entrySet()) {
Expression expr = block.addExpression(entry.getKey(), entry.getValue());
typeInfos.put(entry.getKey(), expr.typeCheckInfo);
newScope.put(entry.getKey(), expr.typeCheckInfo);
}
return typeInfos;
return newScope;
} else {
return Collections.emptyMap();
return previousScope;
}
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

import io.quarkus.qute.Results.Result;
import io.quarkus.qute.SectionHelperFactory.SectionInitContext;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletionStage;

/**
Expand Down Expand Up @@ -70,7 +67,7 @@ public WithSectionHelper initialize(SectionInitContext context) {
}

@Override
public Map<String, String> initializeBlock(Map<String, String> outerNameTypeInfos, BlockInfo block) {
public Scope initializeBlock(Scope previousScope, BlockInfo block) {
if (block.getLabel().equals(MAIN_BLOCK_NAME)) {
String object = block.getParameters().get(OBJECT);
if (object == null) {
Expand All @@ -80,14 +77,14 @@ public Map<String, String> initializeBlock(Map<String, String> outerNameTypeInfo
if (objectExpr.namespace == null && block.hasParameter(ALIAS)) {
// Only validate expressions if alias param is set
String alias = block.getParameters().get(ALIAS);
Map<String, String> typeInfos = new HashMap<String, String>(outerNameTypeInfos);
typeInfos.put(alias, objectExpr.typeCheckInfo);
return typeInfos;
Scope newScope = new Scope(previousScope);
newScope.put(alias, objectExpr.typeCheckInfo);
return newScope;
} else {
return outerNameTypeInfos;
return previousScope;
}
} else {
return Collections.emptyMap();
return previousScope;
}
}

Expand Down

0 comments on commit decb48d

Please sign in to comment.