diff --git a/cirrus.toml b/cirrus.toml
deleted file mode 100644
index c92a86a9..00000000
--- a/cirrus.toml
+++ /dev/null
@@ -1,34 +0,0 @@
-[[orgs]]
-name = "Expression"
-definitionFile = "config/dev-communities.json"
-
-[commands]
-ls = "sf org list --skip-connection-status"
-deploy = "sf project deploy start"
-assign-perms = "sf org assign permset -n Expression_Admin"
-test = "sf apex test run --test-level RunLocalTests --wait 20"
-docs = "apexdocs markdown"
-generate-library-docs = "bun scripts/generate-library-docs.mjs"
-prep-package = "bun scripts/prepare-for-packaging.mjs"
-run-package = "cirrus package create -p Expression -t minor -x -c --promote -w 60"
-post-package = "bun scripts/post-packaging.mjs"
-deploy-conference-community = "sf project deploy start -d unpackaged/examples/expression-conf/main"
-import-data = "sf data import tree -p unpackaged/examples/expression-conf/data/data-plan.json"
-
-[flow.start-dev]
-description = "Starts a new development environment"
-steps = [
- { type = "create_scratch", org = "Expression" },
- { type = "command", name = "deploy" },
- { type = "command", name = "assign-perms" },
- { type = "command", name = "deploy-conference-community" },
- { type = "command", name = "import-data" },
-]
-
-[flow.package]
-description = "Create a managed package version for Expression"
-steps = [
- { type = "command", name = "prep-package" },
- { type = "command", name = "run-package" },
- { type = "command", name = "post-package" },
-]
diff --git a/cirrus.yaml b/cirrus.yaml
new file mode 100644
index 00000000..e2b71a56
--- /dev/null
+++ b/cirrus.yaml
@@ -0,0 +1,66 @@
+# yaml-language-server: $schema=https://cesarparra.github.io/cirrus/schema/v1/cirrus.schema.json
+#
+# Every verb this repo has, and what each one needs before it can run.
+#
+# Commands are not run through a shell: `&&`, pipes and $VARIABLES would reach the program as
+# arguments. A sequence is a flow.
+
+schemaVersion: 1
+
+defaultOrg: Expression
+
+orgs:
+ Expression:
+ definitionFile: config/dev-communities.json
+
+commands:
+ ls:
+ run: sf org list --skip-connection-status
+
+ deploy:
+ run: sf project deploy start
+
+ assign-perms:
+ run: sf org assign permset -n Expression_Admin
+
+ test:
+ run: sf apex test run --test-level RunLocalTests --wait 20
+
+ docs:
+ run: apexdocs markdown
+
+ generate-library-docs:
+ description: Rebuild the function reference and the editor's autocomplete list from the ApexDoc.
+ run: bun scripts/generate-library-docs.mjs
+
+ prep-package:
+ run: bun scripts/prepare-for-packaging.mjs
+
+ run-package:
+ run: cirrus package create -p Expression -t minor -x -c --promote -w 60
+
+ post-package:
+ run: bun scripts/post-packaging.mjs
+
+ deploy-conference-community:
+ run: sf project deploy start -d unpackaged/examples/expression-conf/main
+
+ import-data:
+ run: sf data import tree -p unpackaged/examples/expression-conf/data/data-plan.json
+
+flows:
+ start-dev:
+ description: Starts a new development environment
+ steps:
+ - createScratch: Expression
+ - command: deploy
+ - command: assign-perms
+ - command: deploy-conference-community
+ - command: import-data
+
+ package:
+ description: Create a managed package version for Expression
+ steps:
+ - command: prep-package
+ - command: run-package
+ - command: post-package
diff --git a/docs/src/app/docs/functions/page.md b/docs/src/app/docs/functions/page.md
index 8dfe8d46..aa420e93 100644
--- a/docs/src/app/docs/functions/page.md
+++ b/docs/src/app/docs/functions/page.md
@@ -6,56 +6,6 @@ nextjs:
description: Functions overview.
---
-## Data
-
-### LET
-
-Allows you to define custom variables that can be used in the expression.
-Accepts 2 arguments: a map of variables to define and the expression to evaluate.
-The map keys should be the variable names prefixed with `$`.
-
-```
-LET({ "$a": 1, "$b": 2 }, $a + $b) // 3
-```
-
-### PARSEJSON
-
-Parses a JSON string into a usable map/object structure.
-Accepts 1 argument: the JSON string to parse.
-
-```
-PARSEJSON(Contact.Custom_JSON_Field__c) // Parses JSON from a field
-```
-
-### PRINT
-
-Allows you to print a value to the playground console.
-Accepts 1 argument: the value to print.
-
-```
-PRINT("Hello World")
-```
-
-### RAWQUERY
-
-Allows you to run a raw query against the database.
-Accepts 1 argument: the query to run.
-
-```
-RAWQUERY("SELECT Id, Name FROM Account LIMIT 10")
-```
-
-### TRANSFORM
-
-Transforms any input using the provided expression.
-Provides a special variable `$source` in the inner expression that contains the original input.
-
-Accepts 2 arguments: the input to transform and the expression to evaluate.
-
-```
-TRANSFORM("Hello World", UPPER($source)) // "HELLO WORLD"
-```
-
## Collection
### AGGREGATEGROUPS
@@ -401,6 +351,65 @@ Accepts 2 arguments: List of objects and an expression to evaluate.
WHERE([1, 2, 3], $current > 1)
```
+## Data
+
+### LET
+
+Allows you to define custom variables that can be used in the expression.
+Accepts 2 arguments: a map of variables to define and the expression to evaluate.
+The map keys should be the variable names prefixed with `$`.
+
+```
+LET({ "$a": 1, "$b": 2 }, $a + $b) // 3
+```
+
+### PARSEJSON
+
+Parses a JSON string into a usable map/object structure.
+Accepts 1 argument: the JSON string to parse.
+
+```
+PARSEJSON(Contact.Custom_JSON_Field__c) // Parses JSON from a field
+```
+
+### PRINT
+
+Allows you to print a value to the playground console.
+Accepts 1 argument: the value to print.
+
+```
+PRINT("Hello World")
+```
+
+### RAWQUERY
+
+Allows you to run a raw query against the database.
+Accepts 1 argument: the query to run.
+
+```
+RAWQUERY("SELECT Id, Name FROM Account LIMIT 10")
+```
+
+### TOJSON
+
+Serializes any value into a JSON string. The inverse of `PARSEJSON`.
+Accepts 1 argument: the value to serialize.
+
+```
+Contacts -> MAP([Name, Email]) -> TOJSON() // A table of values, as JSON
+```
+
+### TRANSFORM
+
+Transforms any input using the provided expression.
+Provides a special variable `$source` in the inner expression that contains the original input.
+
+Accepts 2 arguments: the input to transform and the expression to evaluate.
+
+```
+TRANSFORM("Hello World", UPPER($source)) // "HELLO WORLD"
+```
+
## Date and Time
### ADDDAYS
@@ -795,6 +804,103 @@ OR(true, false, true) // true
OR(false, false, false) // false
```
+## Math
+
+### ABS
+
+Returns the absolute value of a number.
+
+Accepts 1 argument: the number to evaluate.
+
+```
+ABS(-1) // 1
+```
+
+### CEILING
+
+Returns the smallest integer greater than or equal to the specified number.
+
+Accepts 1 argument: the number to evaluate.
+
+```
+CEILING(1.5) // 2
+```
+
+### FLOOR
+
+Returns the largest integer less than or equal to the specified number.
+
+Accepts 1 argument: the number to evaluate.
+
+```
+FLOOR(1.5) // 1
+```
+
+### FORMATNUMBER
+
+Formats a number with comma as thousand separator.
+
+Accepts 1 or 2 arguments: the number to format and optionally the number of decimal places.
+
+```
+FORMATNUMBER(20000.53) // "20,000.53"
+FORMATNUMBER(20000.53, 1) // "20,000.5"
+```
+
+### MAX
+
+Returns the largest value in a list of numbers.
+
+Accepts either a list of numbers as a single argument, or multiple numerical arguments.
+
+```
+MAX(1, 2, 3) // 3
+MAX([1, 2, 3]) // 3
+```
+
+### MIN
+
+Returns the smallest value in a list of numbers.
+
+Accepts either a list of numbers as a single argument, or multiple numerical arguments.
+
+```
+MIN(1, 2, 3) // 1
+MIN([1, 2, 3]) // 1
+```
+
+### MOD
+
+Returns the remainder of one number divided by another.
+
+Accepts 2 arguments: the dividend and the divisor.
+
+```
+MOD(5, 2) // 1
+```
+
+### ROUND
+
+Returns a rounded number. Optionally specify the number of decimal places to round to.
+
+Accepts 1 or 2 arguments: the number to round and optionally the number of decimal places to round to.
+
+```
+ROUND(1.234) // 1
+ROUND(1.234, 2) // 1.23
+```
+
+### TRUNC
+
+Returns a truncated number. Optionally specify the number of decimal places to truncate to.
+
+Accepts 1 or 2 arguments: the number to truncate and optionally the number of decimal places to truncate to.
+
+```
+TRUNC(1.234) // 1
+TRUNC(1.234, 2) // 1.23
+```
+
## String
### BEGINS
@@ -1057,100 +1163,3 @@ Accepts 1 argument: the text to convert.
VALUE("123") // 123
```
-## Math
-
-### ABS
-
-Returns the absolute value of a number.
-
-Accepts 1 argument: the number to evaluate.
-
-```
-ABS(-1) // 1
-```
-
-### CEILING
-
-Returns the smallest integer greater than or equal to the specified number.
-
-Accepts 1 argument: the number to evaluate.
-
-```
-CEILING(1.5) // 2
-```
-
-### FLOOR
-
-Returns the largest integer less than or equal to the specified number.
-
-Accepts 1 argument: the number to evaluate.
-
-```
-FLOOR(1.5) // 1
-```
-
-### FORMATNUMBER
-
-Formats a number with comma as thousand separator.
-
-Accepts 1 or 2 arguments: the number to format and optionally the number of decimal places.
-
-```
-FORMATNUMBER(20000.53) // "20,000.53"
-FORMATNUMBER(20000.53, 1) // "20,000.5"
-```
-
-### MAX
-
-Returns the largest value in a list of numbers.
-
-Accepts either a list of numbers as a single argument, or multiple numerical arguments.
-
-```
-MAX(1, 2, 3) // 3
-MAX([1, 2, 3]) // 3
-```
-
-### MIN
-
-Returns the smallest value in a list of numbers.
-
-Accepts either a list of numbers as a single argument, or multiple numerical arguments.
-
-```
-MIN(1, 2, 3) // 1
-MIN([1, 2, 3]) // 1
-```
-
-### MOD
-
-Returns the remainder of one number divided by another.
-
-Accepts 2 arguments: the dividend and the divisor.
-
-```
-MOD(5, 2) // 1
-```
-
-### ROUND
-
-Returns a rounded number. Optionally specify the number of decimal places to round to.
-
-Accepts 1 or 2 arguments: the number to round and optionally the number of decimal places to round to.
-
-```
-ROUND(1.234) // 1
-ROUND(1.234, 2) // 1.23
-```
-
-### TRUNC
-
-Returns a truncated number. Optionally specify the number of decimal places to truncate to.
-
-Accepts 1 or 2 arguments: the number to truncate and optionally the number of decimal places to truncate to.
-
-```
-TRUNC(1.234) // 1
-TRUNC(1.234, 2) // 1.23
-```
-
diff --git a/expression-src/main/editor/lwc/functions/functions-src.js b/expression-src/main/editor/lwc/functions/functions-src.js
index 3860b924..440fa3ba 100644
--- a/expression-src/main/editor/lwc/functions/functions-src.js
+++ b/expression-src/main/editor/lwc/functions/functions-src.js
@@ -1,54 +1,4 @@
export const data = [
- {
- "category": "Data",
- "values": [
- {
- "name": "LET",
- "autoCompleteValue": "LET(",
- "description": "Allows you to define custom variables that can be used in the expression. Accepts 2 arguments: a map of variables to define and the expression to evaluate. The map keys should be the variable names prefixed with `$`.",
- "examples": [
- "LET({ \"$a\": 1, \"$b\": 2 }, $a + $b) // 3"
- ],
- "icon": "utility:data_mapping"
- },
- {
- "name": "PARSEJSON",
- "autoCompleteValue": "PARSEJSON(",
- "description": "Parses a JSON string into a usable map/object structure. Accepts 1 argument: the JSON string to parse.",
- "examples": [
- "PARSEJSON(Contact.Custom_JSON_Field__c) // Parses JSON from a field"
- ],
- "icon": "utility:data_mapping"
- },
- {
- "name": "PRINT",
- "autoCompleteValue": "PRINT(",
- "description": "Allows you to print a value to the playground console. Accepts 1 argument: the value to print.",
- "examples": [
- "PRINT(\"Hello World\")"
- ],
- "icon": "utility:data_mapping"
- },
- {
- "name": "RAWQUERY",
- "autoCompleteValue": "RAWQUERY(",
- "description": "Allows you to run a raw query against the database. Accepts 1 argument: the query to run.",
- "examples": [
- "RAWQUERY(\"SELECT Id, Name FROM Account LIMIT 10\")"
- ],
- "icon": "utility:data_mapping"
- },
- {
- "name": "TRANSFORM",
- "autoCompleteValue": "TRANSFORM(",
- "description": "Transforms any input using the provided expression. Provides a special variable `$source` in the inner expression that contains the original input.
Accepts 2 arguments: the input to transform and the expression to evaluate.",
- "examples": [
- "TRANSFORM(\"Hello World\", UPPER($source)) // \"HELLO WORLD\""
- ],
- "icon": "utility:data_mapping"
- }
- ]
- },
{
"category": "Collection",
"values": [
@@ -360,6 +310,65 @@ export const data = [
}
]
},
+ {
+ "category": "Data",
+ "values": [
+ {
+ "name": "LET",
+ "autoCompleteValue": "LET(",
+ "description": "Allows you to define custom variables that can be used in the expression. Accepts 2 arguments: a map of variables to define and the expression to evaluate. The map keys should be the variable names prefixed with `$`.",
+ "examples": [
+ "LET({ \"$a\": 1, \"$b\": 2 }, $a + $b) // 3"
+ ],
+ "icon": "utility:data_mapping"
+ },
+ {
+ "name": "PARSEJSON",
+ "autoCompleteValue": "PARSEJSON(",
+ "description": "Parses a JSON string into a usable map/object structure. Accepts 1 argument: the JSON string to parse.",
+ "examples": [
+ "PARSEJSON(Contact.Custom_JSON_Field__c) // Parses JSON from a field"
+ ],
+ "icon": "utility:data_mapping"
+ },
+ {
+ "name": "PRINT",
+ "autoCompleteValue": "PRINT(",
+ "description": "Allows you to print a value to the playground console. Accepts 1 argument: the value to print.",
+ "examples": [
+ "PRINT(\"Hello World\")"
+ ],
+ "icon": "utility:data_mapping"
+ },
+ {
+ "name": "RAWQUERY",
+ "autoCompleteValue": "RAWQUERY(",
+ "description": "Allows you to run a raw query against the database. Accepts 1 argument: the query to run.",
+ "examples": [
+ "RAWQUERY(\"SELECT Id, Name FROM Account LIMIT 10\")"
+ ],
+ "icon": "utility:data_mapping"
+ },
+ {
+ "name": "TOJSON",
+ "autoCompleteValue": "TOJSON(",
+ "description": "Serializes any value into a JSON string. The inverse of `PARSEJSON`. Accepts 1 argument: the value to serialize.",
+ "examples": [
+ "Contacts -> MAP([Name, Email]) -> TOJSON() // A table of values, as JSON"
+ ],
+ "icon": "utility:data_mapping"
+ },
+ {
+ "name": "TRANSFORM",
+ "autoCompleteValue": "TRANSFORM(",
+ "description": "Transforms any input using the provided expression. Provides a special variable `$source` in the inner expression that contains the original input.
Accepts 2 arguments: the input to transform and the expression to evaluate.",
+ "examples": [
+ "TRANSFORM(\"Hello World\", UPPER($source)) // \"HELLO WORLD\""
+ ],
+ "icon": "utility:data_mapping"
+ }
+ ]
+ },
{
"category": "Date and Time",
"values": [
@@ -708,6 +717,92 @@ export const data = [
}
]
},
+ {
+ "category": "Math",
+ "values": [
+ {
+ "name": "ABS",
+ "autoCompleteValue": "ABS(",
+ "description": "Returns the absolute value of a number.
Accepts 1 argument: the number to evaluate.",
+ "examples": [
+ "ABS(-1) // 1"
+ ],
+ "icon": "utility:advanced_function"
+ },
+ {
+ "name": "CEILING",
+ "autoCompleteValue": "CEILING(",
+ "description": "Returns the smallest integer greater than or equal to the specified number.
Accepts 1 argument: the number to evaluate.",
+ "examples": [
+ "CEILING(1.5) // 2"
+ ],
+ "icon": "utility:advanced_function"
+ },
+ {
+ "name": "FLOOR",
+ "autoCompleteValue": "FLOOR(",
+ "description": "Returns the largest integer less than or equal to the specified number.
Accepts 1 argument: the number to evaluate.",
+ "examples": [
+ "FLOOR(1.5) // 1"
+ ],
+ "icon": "utility:advanced_function"
+ },
+ {
+ "name": "FORMATNUMBER",
+ "autoCompleteValue": "FORMATNUMBER(",
+ "description": "Formats a number with comma as thousand separator.
Accepts 1 or 2 arguments: the number to format and optionally the number of decimal places.",
+ "examples": [
+ "FORMATNUMBER(20000.53) // \"20,000.53\"\nFORMATNUMBER(20000.53, 1) // \"20,000.5\""
+ ],
+ "icon": "utility:advanced_function"
+ },
+ {
+ "name": "MAX",
+ "autoCompleteValue": "MAX(",
+ "description": "Returns the largest value in a list of numbers.
Accepts either a list of numbers as a single argument, or multiple numerical arguments.",
+ "examples": [
+ "MAX(1, 2, 3) // 3\nMAX([1, 2, 3]) // 3"
+ ],
+ "icon": "utility:advanced_function"
+ },
+ {
+ "name": "MIN",
+ "autoCompleteValue": "MIN(",
+ "description": "Returns the smallest value in a list of numbers.
Accepts either a list of numbers as a single argument, or multiple numerical arguments.",
+ "examples": [
+ "MIN(1, 2, 3) // 1\nMIN([1, 2, 3]) // 1"
+ ],
+ "icon": "utility:advanced_function"
+ },
+ {
+ "name": "MOD",
+ "autoCompleteValue": "MOD(",
+ "description": "Returns the remainder of one number divided by another.
Accepts 2 arguments: the dividend and the divisor.",
+ "examples": [
+ "MOD(5, 2) // 1"
+ ],
+ "icon": "utility:advanced_function"
+ },
+ {
+ "name": "ROUND",
+ "autoCompleteValue": "ROUND(",
+ "description": "Returns a rounded number. Optionally specify the number of decimal places to round to.
Accepts 1 or 2 arguments: the number to round and optionally the number of decimal places to round to.",
+ "examples": [
+ "ROUND(1.234) // 1\nROUND(1.234, 2) // 1.23"
+ ],
+ "icon": "utility:advanced_function"
+ },
+ {
+ "name": "TRUNC",
+ "autoCompleteValue": "TRUNC(",
+ "description": "Returns a truncated number. Optionally specify the number of decimal places to truncate to.
Accepts 1 or 2 arguments: the number to truncate and optionally the number of decimal places to truncate to.",
+ "examples": [
+ "TRUNC(1.234) // 1\nTRUNC(1.234, 2) // 1.23"
+ ],
+ "icon": "utility:advanced_function"
+ }
+ ]
+ },
{
"category": "String",
"values": [
@@ -919,91 +1014,5 @@ export const data = [
"icon": "utility:text"
}
]
- },
- {
- "category": "Math",
- "values": [
- {
- "name": "ABS",
- "autoCompleteValue": "ABS(",
- "description": "Returns the absolute value of a number.
Accepts 1 argument: the number to evaluate.",
- "examples": [
- "ABS(-1) // 1"
- ],
- "icon": "utility:advanced_function"
- },
- {
- "name": "CEILING",
- "autoCompleteValue": "CEILING(",
- "description": "Returns the smallest integer greater than or equal to the specified number.
Accepts 1 argument: the number to evaluate.",
- "examples": [
- "CEILING(1.5) // 2"
- ],
- "icon": "utility:advanced_function"
- },
- {
- "name": "FLOOR",
- "autoCompleteValue": "FLOOR(",
- "description": "Returns the largest integer less than or equal to the specified number.
Accepts 1 argument: the number to evaluate.",
- "examples": [
- "FLOOR(1.5) // 1"
- ],
- "icon": "utility:advanced_function"
- },
- {
- "name": "FORMATNUMBER",
- "autoCompleteValue": "FORMATNUMBER(",
- "description": "Formats a number with comma as thousand separator.
Accepts 1 or 2 arguments: the number to format and optionally the number of decimal places.",
- "examples": [
- "FORMATNUMBER(20000.53) // \"20,000.53\"\nFORMATNUMBER(20000.53, 1) // \"20,000.5\""
- ],
- "icon": "utility:advanced_function"
- },
- {
- "name": "MAX",
- "autoCompleteValue": "MAX(",
- "description": "Returns the largest value in a list of numbers.
Accepts either a list of numbers as a single argument, or multiple numerical arguments.",
- "examples": [
- "MAX(1, 2, 3) // 3\nMAX([1, 2, 3]) // 3"
- ],
- "icon": "utility:advanced_function"
- },
- {
- "name": "MIN",
- "autoCompleteValue": "MIN(",
- "description": "Returns the smallest value in a list of numbers.
Accepts either a list of numbers as a single argument, or multiple numerical arguments.",
- "examples": [
- "MIN(1, 2, 3) // 1\nMIN([1, 2, 3]) // 1"
- ],
- "icon": "utility:advanced_function"
- },
- {
- "name": "MOD",
- "autoCompleteValue": "MOD(",
- "description": "Returns the remainder of one number divided by another.
Accepts 2 arguments: the dividend and the divisor.",
- "examples": [
- "MOD(5, 2) // 1"
- ],
- "icon": "utility:advanced_function"
- },
- {
- "name": "ROUND",
- "autoCompleteValue": "ROUND(",
- "description": "Returns a rounded number. Optionally specify the number of decimal places to round to.
Accepts 1 or 2 arguments: the number to round and optionally the number of decimal places to round to.",
- "examples": [
- "ROUND(1.234) // 1\nROUND(1.234, 2) // 1.23"
- ],
- "icon": "utility:advanced_function"
- },
- {
- "name": "TRUNC",
- "autoCompleteValue": "TRUNC(",
- "description": "Returns a truncated number. Optionally specify the number of decimal places to truncate to.
Accepts 1 or 2 arguments: the number to truncate and optionally the number of decimal places to truncate to.",
- "examples": [
- "TRUNC(1.234) // 1\nTRUNC(1.234, 2) // 1.23"
- ],
- "icon": "utility:advanced_function"
- }
- ]
}
];
\ No newline at end of file
diff --git a/expression-src/main/src/interpreter/std-lib/DataFunctions.cls b/expression-src/main/src/interpreter/std-lib/DataFunctions.cls
index 8a9d3a86..46549512 100644
--- a/expression-src/main/src/interpreter/std-lib/DataFunctions.cls
+++ b/expression-src/main/src/interpreter/std-lib/DataFunctions.cls
@@ -10,7 +10,8 @@ public with sharing class DataFunctions {
'LET' => new LET(),
'RAWQUERY' => new RawQuery(),
'PRINT' => new PrintFn(),
- 'PARSEJSON' => new ParseJsonFn()
+ 'PARSEJSON' => new ParseJsonFn(),
+ 'TOJSON' => new ToJsonFn()
};
public class DataFunctionsProvider implements FunctionProvider {
@@ -203,4 +204,42 @@ public with sharing class DataFunctions {
return Arity.exactly(1);
}
}
+
+ /**
+ * @description Serializes any value into a JSON string. The inverse of `PARSEJSON`.
+ * Accepts 1 argument: the value to serialize.
+ * @function TOJSON
+ * @example TOJSON([1, "two", true]) // "[1,\"two\",true]"
+ * @example Contacts -> MAP([Name, Email]) -> TOJSON() // A table of values, as JSON
+ */
+ private class ToJsonFn extends StandardFunction {
+ public override Object call(List arguments) {
+ return JSON.serialize(serializable(evaluate(arguments.get(0))));
+ }
+
+ // A map literal evaluates to a Map