Skip to content

Commit

Permalink
Merge pull request allegro#4 from rzukow/functions_and_variables_in_e…
Browse files Browse the repository at this point in the history
…ngine

registering functions and variables in engine
  • Loading branch information
bgalek authored Sep 12, 2016
2 parents f11c40a + 3301174 commit 5698930
Show file tree
Hide file tree
Showing 16 changed files with 1,249 additions and 908 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@ classes

# mac os x
.DS_Store

82 changes: 1 addition & 81 deletions src/main/java/pl/allegro/tech/opel/EvalContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@


import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

Expand All @@ -14,84 +12,6 @@ public interface EvalContext {
Optional<CompletableFuture<?>> getVariable(String name);

static EvalContext empty() {
return Builder.fromMaps(Collections.emptyMap(), Collections.emptyMap());
}

class Builder {
private final Map<String, CompletableFuture<Object>> variables = new HashMap<>();
private final Map<String, OpelAsyncFunction<?>> functions = new HashMap<>();
private Optional<EvalContext> parentEvalContext = Optional.empty();

private static EvalContext fromMaps(Map<String, CompletableFuture<Object>> variables, Map<String, OpelAsyncFunction<?>> functions) {
return new EvalContext() {
@Override
public Optional<OpelAsyncFunction<?>> getFunction(String name) {
return Optional.ofNullable(functions.get(name));
}

@Override
public Optional<CompletableFuture<?>> getVariable(String name) {
return Optional.ofNullable(variables.get(name));
}
};
}

public static Builder create() {
return new Builder();
}

public Builder withParentEvalContext(EvalContext evalContext) {
this.parentEvalContext = Optional.of(evalContext);
return this;
}

public Builder withVariable(String variableName, CompletableFuture<Object> variable) {
variables.put(variableName, variable);
return this;
}

public Builder withVariables(Map<String, CompletableFuture<Object>> variables) {
this.variables.putAll(variables);
return this;
}

public Builder withCompletedVariable(String variableName, Object variable) {
variables.put(variableName, CompletableFuture.completedFuture(variable));
return this;
}

public Builder withFunction(String functionName, OpelAsyncFunction<?> function) {
functions.put(functionName, function);
return this;
}

public Builder withFunctions(Map<String, OpelAsyncFunction<?>> functions) {
this.functions.putAll(functions);
return this;
}

public EvalContext build() {
return parentEvalContext.map(this::mergeContexts).orElse(fromMaps(variables, functions));
}

EvalContext mergeContexts(EvalContext parent) {
return new EvalContext() {
@Override
public Optional<OpelAsyncFunction<?>> getFunction(String name) {
if (functions.containsKey(name)) {
return Optional.ofNullable(functions.get(name));
}
return parent.getFunction(name);
}

@Override
public Optional<CompletableFuture<?>> getVariable(String name) {
if (variables.containsKey(name)) {
return Optional.ofNullable(variables.get(name));
}
return parent.getVariable(name);
}
};
}
return EvalContextBuilder.fromMaps(Collections.emptyMap(), Collections.emptyMap());
}
}
100 changes: 100 additions & 0 deletions src/main/java/pl/allegro/tech/opel/EvalContextBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package pl.allegro.tech.opel;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

public class EvalContextBuilder {
private final Map<String, CompletableFuture<Object>> variables = new HashMap<>();
private final Map<String, OpelAsyncFunction<?>> functions = new HashMap<>();
private Optional<EvalContext> parentEvalContext = Optional.empty();

static EvalContext fromMaps(Map<String, CompletableFuture<Object>> variables, Map<String, OpelAsyncFunction<?>> functions) {
return new EvalContext() {
@Override
public Optional<OpelAsyncFunction<?>> getFunction(String name) {
return Optional.ofNullable(functions.get(name));
}

@Override
public Optional<CompletableFuture<?>> getVariable(String name) {
return Optional.ofNullable(variables.get(name));
}
};
}

public static EvalContextBuilder create() {
return new EvalContextBuilder();
}

public EvalContextBuilder withParentEvalContext(EvalContext evalContext) {
this.parentEvalContext = Optional.of(evalContext);
return this;
}

public EvalContextBuilder withVariable(String variableName, CompletableFuture<Object> variable) {
variables.put(variableName, variable);
return this;
}

public EvalContextBuilder withVariables(Map<String, CompletableFuture<Object>> variables) {
this.variables.putAll(variables);
return this;
}

public EvalContextBuilder withCompletedVariable(String variableName, Object variable) {
variables.put(variableName, CompletableFuture.completedFuture(variable));
return this;
}

public EvalContextBuilder withFunction(String functionName, OpelAsyncFunction<?> function) {
functions.put(functionName, function);
return this;
}

public EvalContextBuilder withFunctions(Map<String, OpelAsyncFunction<?>> functions) {
this.functions.putAll(functions);
return this;
}

public EvalContext build() {
return parentEvalContext.map(this::mergeContexts).orElse(fromMaps(variables, functions));
}

EvalContext mergeContexts(EvalContext parent) {
return new EvalContext() {
@Override
public Optional<OpelAsyncFunction<?>> getFunction(String name) {
if (functions.containsKey(name)) {
return Optional.ofNullable(functions.get(name));
}
return parent.getFunction(name);
}

@Override
public Optional<CompletableFuture<?>> getVariable(String name) {
if (variables.containsKey(name)) {
return Optional.ofNullable(variables.get(name));
}
return parent.getVariable(name);
}
};
}

static EvalContext mergeContexts(EvalContext primary, EvalContext secondary) {
return new EvalContext() {
@Override
public Optional<OpelAsyncFunction<?>> getFunction(String name) {
Optional<OpelAsyncFunction<?>> function = primary.getFunction(name);
return function.isPresent() ? function : secondary.getFunction(name);
}

@Override
public Optional<CompletableFuture<?>> getVariable(String name) {
Optional<CompletableFuture<?>> variable = primary.getVariable(name);
return (variable.isPresent()) ? variable : secondary.getVariable(name);
}
};
}
}
4 changes: 0 additions & 4 deletions src/main/java/pl/allegro/tech/opel/ExpressionNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,4 @@

interface ExpressionNode {
CompletableFuture<?> getValue(EvalContext context);

default CompletableFuture<?> getValue() {
return getValue(EvalContext.empty());
}
}
25 changes: 9 additions & 16 deletions src/main/java/pl/allegro/tech/opel/OpelEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,17 @@
import org.parboiled.support.ParsingResult;

import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

public class OpelEngine {
class OpelEngine {
private final ThreadLocal<OpelParser> parser;
private final ImplicitConversion implicitConversion;

public OpelEngine() {
this(MethodExecutionFilters.ALLOW_ALL);
}
private EvalContext embeddedEvalContext = EvalContext.empty();

public OpelEngine(MethodExecutionFilter methodExecutionFilter) {
implicitConversion = new ImplicitConversion();
implicitConversion.registerNumberConversion();
parser = ThreadLocal.withInitial(() -> Parboiled.createParser(OpelParser.class, methodExecutionFilter, implicitConversion));
OpelEngine(MethodExecutionFilter methodExecutionFilter, ImplicitConversion implicitConversion, EvalContext embeddedEvalContext) {
this.embeddedEvalContext = embeddedEvalContext;
this.implicitConversion = implicitConversion;
parser = ThreadLocal.withInitial(() -> Parboiled.createParser(OpelParser.class, methodExecutionFilter, this.implicitConversion));
}

public ExpressionValidationResult validate(String expression) {
Expand All @@ -31,24 +28,20 @@ public ExpressionValidationResult validate(String expression) {
}

public OpelParsingResult parse(String expression) {
return new OpelParsingResult(expression, getParsingResult(expression));
return new OpelParsingResult(expression, getParsingResult(expression), embeddedEvalContext);
}

public CompletableFuture<?> eval(String expression) {
ParsingResult<ExpressionNode> parsingResult = getParsingResult(expression);
return parsingResult.resultValue.getValue();
return parsingResult.resultValue.getValue(embeddedEvalContext);
}

public CompletableFuture<?> eval(String expression, EvalContext evalContext) {
ParsingResult<ExpressionNode> parsingResult = getParsingResult(expression);
return parsingResult.resultValue.getValue(evalContext);
return parsingResult.resultValue.getValue(EvalContextBuilder.mergeContexts(evalContext, embeddedEvalContext));
}

private ParsingResult<ExpressionNode> getParsingResult(String expression) {
return new ReportingParseRunner<ExpressionNode>(parser.get().ParsingUnit()).run(expression);
}

public <T, R> void registerImplicitConversion(Class<T> from, Class<R> to, Function<T, R> conversion) {
implicitConversion.register(new ImplicitConversionUnit<>(from, to, conversion));
}
}
65 changes: 65 additions & 0 deletions src/main/java/pl/allegro/tech/opel/OpelEngineBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package pl.allegro.tech.opel;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

public class OpelEngineBuilder {
private final Map<String, OpelAsyncFunction<?>> embeddedFunctions = new HashMap<>();
private final Map<String, CompletableFuture<Object>> embeddedVariables = new HashMap<>();
private MethodExecutionFilter methodExecutionFilter = MethodExecutionFilters.ALLOW_ALL;
private final ImplicitConversion implicitConversion;

private OpelEngineBuilder() {
implicitConversion = new ImplicitConversion();
implicitConversion.registerNumberConversion();
}

public static OpelEngineBuilder create() {
return new OpelEngineBuilder();
}

public OpelEngineBuilder withFunction(String functionName, OpelAsyncFunction<?> function) {
embeddedFunctions.put(functionName, function);
return this;
}

public OpelEngineBuilder withFunctions(Map<String, OpelAsyncFunction<?>> functions) {
embeddedFunctions.putAll(functions);
return this;
}

public OpelEngineBuilder withVariable(String variableName, CompletableFuture<Object> value) {
embeddedVariables.put(variableName, value);
return this;
}

public OpelEngineBuilder withVariables(Map<String, CompletableFuture<Object>> variables) {
embeddedVariables.putAll(variables);
return this;
}

public OpelEngineBuilder withCompletedVariable(String variableName, Object value) {
embeddedVariables.put(variableName, CompletableFuture.completedFuture(value));
return this;
}

public OpelEngineBuilder withMethodExecutionFilter(MethodExecutionFilter methodExecutionFilter) {
this.methodExecutionFilter = methodExecutionFilter;
return this;
}

public <T, R> OpelEngineBuilder withImplicitConversion(Class<T> from, Class<R> to, Function<T, R> conversion) {
implicitConversion.register(new ImplicitConversionUnit<>(from, to, conversion));
return this;
}

public OpelEngine build() {
EvalContext context = EvalContextBuilder.create()
.withFunctions(embeddedFunctions)
.withVariables(embeddedVariables)
.build();
return new OpelEngine(methodExecutionFilter, implicitConversion, context);
}
}
12 changes: 11 additions & 1 deletion src/main/java/pl/allegro/tech/opel/OpelParsingResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,23 @@
public class OpelParsingResult {
private final ParsingResult<ExpressionNode> parsingResult;
private final String expression;
private final EvalContext embeddedEvalContext;

OpelParsingResult(String expression, ParsingResult<ExpressionNode> parsingResult) {
OpelParsingResult(String expression, ParsingResult<ExpressionNode> parsingResult, EvalContext embeddedEvalContext) {
this.parsingResult = parsingResult;
this.expression = expression;
this.embeddedEvalContext = embeddedEvalContext;
}

public CompletableFuture<?> eval(EvalContext context) {
return evalWithFinalContext(EvalContextBuilder.mergeContexts(context, embeddedEvalContext));
}

public CompletableFuture<?> eval() {
return evalWithFinalContext(embeddedEvalContext);
}

private CompletableFuture<?> evalWithFinalContext(EvalContext context) {
return getParsedExpression()
.map(node -> node.getValue(context))
.orElseThrow(() -> new OpelException("Expression '" + expression + "' contain's syntax error"));
Expand Down
Loading

0 comments on commit 5698930

Please sign in to comment.