Skip to content

Commit

Permalink
Refactoring (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
cesarParra authored Nov 5, 2023
1 parent e8c4307 commit ab3638d
Show file tree
Hide file tree
Showing 40 changed files with 407 additions and 155 deletions.
2 changes: 1 addition & 1 deletion .idea/formula-evaluator.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2199,12 +2199,11 @@ The source code includes a `Visitor` implementation
whose sole purpose is to do this, `AstPrinter`. When enabled, it will
print the AST to the logs.

You can enable it by passing an `expression.Evaluator.Config` option to the `run`
You can enable it by passing an `expression.Configuration` option to the `run`
method with the `printAst` option enabled :

```apex
expression.Evaluator.Config config = new expression.Evaluator.Config();
config.printAst = true;
expression.Configuration config = new expression.Configuration().printAst();
Object value = expression.Evaluator.run('AND(true, false, 1=1)', config);
// Outputs to the logs:
// (AND true false (= 1 1))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ public with sharing class FormulaEvaluatorUiController {
@AuraEnabled(Cacheable=true)
public static Object evaluate(String recordId, String formula, Boolean respectSharing) {
try {
Evaluator.Config config = new Evaluator.Config();
config.sharing = respectSharing ? Evaluator.SharingMode.WITH : Evaluator.SharingMode.WITHOUT;
Configuration config = new Configuration().respectSharing(respectSharing);
if (String.isBlank(recordId)) {
return Evaluator.run(formula, config);
} else {
Expand Down
43 changes: 43 additions & 0 deletions expression-src/main/api/Configuration.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
global with sharing class Configuration {
public static Configuration.SharingMode evaluationSharingMode;

global enum SharingMode {
WITH,
WITHOUT
}

global SharingMode sharing = SharingMode.WITH;
global Boolean printAst = false;
public Boolean withDiagnostics = false;

global Configuration respectSharing(Boolean respect) {
sharing = respect ? SharingMode.WITH : SharingMode.WITHOUT;
return this;
}

global Configuration printAst() {
printAst = true;
return this;
}

global Configuration withDiagnostics() {
withDiagnostics = true;
return this;
}

public void subscribe(EvaluatorEventNotifier notifier) {
// Always subscribe to the event that sets the sharing mode
// at the beginning of the evaluation regardless of configuration.
notifier.subscribe(OnEvaluationStartEvent.class, new EvaluationSharingModeSetter());

if (this.printAst) {
notifier.subscribe(OnAfterParseEvent.class, new AstPrinter());
}

if (this.withDiagnostics) {
EvaluationDiagnosticsListener diagnosticsListener = new EvaluationDiagnosticsListener();
notifier.subscribe(OnEvaluationStartEvent.class, diagnosticsListener);
notifier.subscribe(OnEvaluationEndEvent.class, diagnosticsListener);
}
}
}
72 changes: 9 additions & 63 deletions expression-src/main/api/Evaluator.cls
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
/**
* @description Evaluates a formula and returns the result.
*/
global with sharing class Evaluator {
public static SharingMode evaluationSharingMode;

global with sharing abstract class Evaluator {
/**
* @description Evaluates a formula and returns the result.
* @param formula The formula to evaluate.
* @return The result of the formula.
*/
global static Object run(String formula) {
return run(formula, new Config());
return run(formula, new Configuration());
}

/**
Expand All @@ -20,7 +18,7 @@ global with sharing class Evaluator {
* the evaluation.
* @return The result of the formula.
*/
global static Object run(String formula, Config config) {
global static Object run(String formula, Configuration config) {
return run(formula, (SObject) null, config);
}

Expand All @@ -33,7 +31,7 @@ global with sharing class Evaluator {
* @return The result of the formula.
*/
global static Object run(String formula, SObject context) {
return run(formula, context, new Config());
return run(formula, context, new Configuration());
}

/**
Expand All @@ -46,9 +44,8 @@ global with sharing class Evaluator {
* the evaluation.
* @return The result of the formula.
*/
global static Object run(String formula, SObject context, Config config) {
Expr pipedExpression = parseExpression(formula, config);
return interpretWithContext(context, pipedExpression, config);
global static Object run(String formula, SObject context, Configuration config) {
return EvaluatorResolver.forRecord(context).evaluate(formula, config).result;
}

/**
Expand All @@ -60,7 +57,7 @@ global with sharing class Evaluator {
* @return The result of the formula.
*/
global static Object run(String formula, Id recordId) {
return run(formula, recordId, new Config());
return run(formula, recordId, new Configuration());
}

/**
Expand All @@ -73,58 +70,7 @@ global with sharing class Evaluator {
* the evaluation.
* @return The result of the formula.
*/
global static Object run(String formula, Id recordId, Config config) {
Expr pipedExpression = parseExpression(formula, config);
SObject record = getRecord(recordId, pipedExpression);
return interpretWithContext(record, pipedExpression, config);
}

private static Expr parseExpression(String formula, Config config) {
evaluationSharingMode = config.sharing;
Scanner scanner = new Scanner(formula);
List<Token> tokens = scanner.scanTokens();

Parser parser = new Parser(tokens);
Expr expression = parser.parse();

if (config.printAst) {
AstPrinter printer = new AstPrinter();
printer.printAst(expression);
}

PipeResolver pipeInterpreter = new PipeResolver();
Expr pipedExpression = pipeInterpreter.resolve(expression);
return pipedExpression;
}

private static SObject getRecord(Id recordId, Expr pipedExpression) {
ContextResolver ctxInterpreter = new ContextResolver(recordId);
return ctxInterpreter.build(pipedExpression);
}

private static Object interpretWithContext(SObject record, Expr pipedExpression, Config config) {
Environment env = new Environment(record);
IInterpreter interpreter = getInterpreter(env, config);
return interpreter.interpret(pipedExpression);
}

global enum SharingMode {
WITH,
WITHOUT
}

global class Config {
global SharingMode sharing = SharingMode.WITH;
global Boolean printAst = false;
public Boolean withDiagnostics = false;
}

private static IInterpreter getInterpreter(Environment env, Config config) {
IInterpreter mainInterpreter = new Interpreter(env);
if (config.withDiagnostics) {
return new DiagnosticsDecorator(mainInterpreter);
} else {
return mainInterpreter;
}
global static Object run(String formula, Id recordId, Configuration config) {
return EvaluatorResolver.forId(recordId).evaluate(formula, config).result;
}
}
31 changes: 23 additions & 8 deletions expression-src/main/editor/controllers/PlaygroundController.cls
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,30 @@ public with sharing class PlaygroundController {
return toReturn;
}

// Record Id is of type String because we could receive an empty String
// from the front-end, so we can't use Id as the type in that case.
@AuraEnabled
public static Result validate(String expr, Id recordId) {
public static Result validate(String expr, String recordId) {
Result toReturn = new Result();
Evaluator.Config config = new Evaluator.Config();
config.withDiagnostics = true;
Configuration config = new Configuration().withDiagnostics().printAst();
try {
EvaluationResult evalResult;
if (String.isNotBlank(recordId)) {
toReturn.result = Evaluator.run(expr, recordId, config);
evalResult = EvaluatorResolver.forId(recordId).evaluate(expr, config);
} else {
toReturn.result = Evaluator.run(expr, config);
evalResult = EvaluatorResolver.withoutContext().evaluate(expr, config);
}

toReturn.result = evalResult.result;

if (evalResult.additionalData.containsKey('diagnostics')) {
EvaluationDiagnosticsListener.DiagnosticsResult diagnosticsResult =
(EvaluationDiagnosticsListener.DiagnosticsResult)evalResult.additionalData.get('diagnostics');
toReturn.diagnostics = new Diagnostics(diagnosticsResult);
}

if (evalResult.additionalData.containsKey('ast')) {
toReturn.ast = evalResult.additionalData.get('ast');
}
} catch (Exceptions.PositionAwareException e) {
EvaluationError error = new EvaluationError();
Expand All @@ -60,8 +74,6 @@ public with sharing class PlaygroundController {
toReturn.error = error;
}

toReturn.diagnostics = new Diagnostics(DiagnosticsDecorator.result);

return toReturn;
}

Expand All @@ -74,6 +86,9 @@ public with sharing class PlaygroundController {

@AuraEnabled
public Diagnostics diagnostics;

@AuraEnabled
public Object ast;
}

public class Diagnostics {
Expand All @@ -89,7 +104,7 @@ public with sharing class PlaygroundController {
@AuraEnabled
public Integer queryRows;

public Diagnostics(DiagnosticsDecorator.DiagnosticsResult result) {
public Diagnostics(EvaluationDiagnosticsListener.DiagnosticsResult result) {
this.cpuTime = result?.cpuTime;
this.dmlStatements = result?.dmlStatements;
this.queries = result?.queries;
Expand Down
10 changes: 8 additions & 2 deletions expression-src/main/editor/lwc/playground/playground.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,19 @@
</lightning-tab>
<lightning-tab label="Diagnostics">
<ul>
<li><strong>CPU Time:</strong> {diagnostics.cpuTime}<span
if:true={diagnostics.cpuTime}>ms</span></li>
<li><strong>CPU Time (ms):</strong> {diagnostics.cpuTime}<span
if:true={diagnostics.cpuTime}></span></li>
<li><strong>DML Statements:</strong> {diagnostics.dmlStatements}</li>
<li><strong>Queries:</strong> {diagnostics.queries}</li>
<li><strong>Query Rows:</strong> {diagnostics.queryRows}</li>
</ul>
</lightning-tab>
<lightning-tab label="AST">
<pre class={resultColor}>
<lightning-formatted-rich-text
value={ast}></lightning-formatted-rich-text>
</pre>
</lightning-tab>
</lightning-tabset>
</div>
</div>
Expand Down
25 changes: 23 additions & 2 deletions expression-src/main/editor/lwc/playground/playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ export default class Monaco extends LightningElement {
recordId;
iframeUrl = `${monaco}/main.html`;
result = {};
diagnostics = {};
diagnostics = {
cpuTime: "Unavailable",
dmlStatements: "Unavailable",
queries: "Unavailable",
queryRows: "Unavailable",
};
ast = "";

async iframeLoaded() {
const functionKeywords = await getFunctions();
Expand Down Expand Up @@ -40,7 +46,22 @@ export default class Monaco extends LightningElement {
}
}

this.diagnostics = result.diagnostics;
this._setDiagnostics(result);
this.ast = result.ast ?
this._syntaxHighlight(JSON.stringify(result.ast, null, 4)) :
"";
}

_setDiagnostics(result) {
this.diagnostics = Object.keys(result.diagnostics).reduce((acc, key) => {
acc[key] = result.diagnostics[key] ?? "Unavailable";
return acc;
}, {
cpuTime: "Unavailable",
dmlStatements: "Unavailable",
queries: "Unavailable",
queryRows: "Unavailable",
});
}

handleInputChange(event) {
Expand Down
2 changes: 1 addition & 1 deletion expression-src/main/src/helpers/QRunner.cls
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public inherited sharing abstract class QRunner {
return mockRunner;
}

if (Evaluator.evaluationSharingMode == Evaluator.SharingMode.WITH) {
if (Configuration.evaluationSharingMode == Configuration.SharingMode.WITH) {
return new WithSharingRunner();
} else {
return new WithoutSharingRunner();
Expand Down
46 changes: 0 additions & 46 deletions expression-src/main/src/interpreter/DiagnosticsDecorator.cls

This file was deleted.

3 changes: 0 additions & 3 deletions expression-src/main/src/interpreter/IInterpreter.cls

This file was deleted.

2 changes: 1 addition & 1 deletion expression-src/main/src/interpreter/Interpreter.cls
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
public with sharing class Interpreter implements IInterpreter, Visitor {
public with sharing class Interpreter implements Visitor {
private final Environment env;

private Boolean inListLiteral = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public with sharing class CollectionFunctions {
}

public override Arity getArity() {
return Arity.atLeast(1);
return Arity.atLeast(0);
}
}

Expand Down
Loading

0 comments on commit ab3638d

Please sign in to comment.