Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring #81

Merged
merged 10 commits into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading