From 65b4dad5549d93a67e86051f41a7a7e454b819b7 Mon Sep 17 00:00:00 2001 From: Joshua Lochner Date: Thu, 15 Feb 2024 18:35:59 +0200 Subject: [PATCH] [jinja] Add more built-in functions (#484) This PR adds support for several built-in jinja filters (from [this list](https://jinja.palletsprojects.com/en/3.0.x/templates/#list-of-builtin-filters)). Related discussion: https://huggingface.co/codellama/CodeLlama-70b-Instruct-hf/discussions/21 --- packages/jinja/src/runtime.ts | 30 ++++++++++-- packages/jinja/test/templates.test.js | 70 +++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/packages/jinja/src/runtime.ts b/packages/jinja/src/runtime.ts index 6bc3646f2..c34dd0f4d 100644 --- a/packages/jinja/src/runtime.ts +++ b/packages/jinja/src/runtime.ts @@ -341,6 +341,7 @@ export class Interpreter { // } // return filter.value([operand], environment); + // https://jinja.palletsprojects.com/en/3.0.x/templates/#list-of-builtin-filters if (operand instanceof ArrayValue) { switch (node.filter.value) { case "first": @@ -368,12 +369,35 @@ export class Interpreter { }) ); default: - throw new Error(`Unknown filter: ${node.filter.value}`); + throw new Error(`Unknown ArrayValue filter: ${node.filter.value}`); + } + } else if (operand instanceof StringValue) { + switch (node.filter.value) { + case "length": + return new NumericValue(operand.value.length); + case "upper": + return new StringValue(operand.value.toUpperCase()); + case "lower": + return new StringValue(operand.value.toLowerCase()); + case "title": + return new StringValue(operand.value.replace(/\b\w/g, (c) => c.toUpperCase())); + case "capitalize": + return new StringValue(operand.value.charAt(0).toUpperCase() + operand.value.slice(1)); + case "trim": + return new StringValue(operand.value.trim()); + default: + throw new Error(`Unknown StringValue filter: ${node.filter.value}`); + } + } else if (operand instanceof NumericValue) { + switch (node.filter.value) { + case "abs": + return new NumericValue(Math.abs(operand.value)); + default: + throw new Error(`Unknown NumericValue filter: ${node.filter.value}`); } } - // TODO add support for StringValue operand - throw new Error(`Cannot apply filter to type: ${operand.type}`); + throw new Error(`Cannot apply filter "${node.filter.value}" to type: ${operand.type}`); } /** diff --git a/packages/jinja/test/templates.test.js b/packages/jinja/test/templates.test.js index 728cb0167..ee4ceefe3 100644 --- a/packages/jinja/test/templates.test.js +++ b/packages/jinja/test/templates.test.js @@ -69,6 +69,8 @@ const TEST_STRINGS = { // Filter operator FILTER_OPERATOR: `{{ arr | length }}{{ 1 + arr | length }}{{ 2 + arr | sort | length }}{{ (arr | sort)[0] }}`, + FILTER_OPERATOR_2: `|{{ 'abc' | length }}|{{ 'aBcD' | upper }}|{{ 'aBcD' | lower }}|{{ 'test test' | capitalize}}|{{ 'test test' | title }}|{{ ' a b ' | trim }}|{{ ' A B ' | trim | lower | length }}|`, + FILTER_OPERATOR_3: `|{{ -1 | abs }}|{{ 1 | abs }}|`, // Logical operators between non-Booleans BOOLEAN_NUMERICAL: `|{{ 1 and 2 }}|{{ 1 and 0 }}|{{ 0 and 1 }}|{{ 0 and 0 }}|{{ 1 or 2 }}|{{ 1 or 0 }}|{{ 0 or 1 }}|{{ 0 or 0 }}|{{ not 1 }}|{{ not 0 }}|`, @@ -1139,6 +1141,70 @@ const TEST_PARSED = { { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, ], + FILTER_OPERATOR_2: [ + { value: "|", type: "Text" }, + { value: "{{", type: "OpenExpression" }, + { value: "abc", type: "StringLiteral" }, + { value: "|", type: "Pipe" }, + { value: "length", type: "Identifier" }, + { value: "}}", type: "CloseExpression" }, + { value: "|", type: "Text" }, + { value: "{{", type: "OpenExpression" }, + { value: "aBcD", type: "StringLiteral" }, + { value: "|", type: "Pipe" }, + { value: "upper", type: "Identifier" }, + { value: "}}", type: "CloseExpression" }, + { value: "|", type: "Text" }, + { value: "{{", type: "OpenExpression" }, + { value: "aBcD", type: "StringLiteral" }, + { value: "|", type: "Pipe" }, + { value: "lower", type: "Identifier" }, + { value: "}}", type: "CloseExpression" }, + { value: "|", type: "Text" }, + { value: "{{", type: "OpenExpression" }, + { value: "test test", type: "StringLiteral" }, + { value: "|", type: "Pipe" }, + { value: "capitalize", type: "Identifier" }, + { value: "}}", type: "CloseExpression" }, + { value: "|", type: "Text" }, + { value: "{{", type: "OpenExpression" }, + { value: "test test", type: "StringLiteral" }, + { value: "|", type: "Pipe" }, + { value: "title", type: "Identifier" }, + { value: "}}", type: "CloseExpression" }, + { value: "|", type: "Text" }, + { value: "{{", type: "OpenExpression" }, + { value: " a b ", type: "StringLiteral" }, + { value: "|", type: "Pipe" }, + { value: "trim", type: "Identifier" }, + { value: "}}", type: "CloseExpression" }, + { value: "|", type: "Text" }, + { value: "{{", type: "OpenExpression" }, + { value: " A B ", type: "StringLiteral" }, + { value: "|", type: "Pipe" }, + { value: "trim", type: "Identifier" }, + { value: "|", type: "Pipe" }, + { value: "lower", type: "Identifier" }, + { value: "|", type: "Pipe" }, + { value: "length", type: "Identifier" }, + { value: "}}", type: "CloseExpression" }, + { value: "|", type: "Text" }, + ], + FILTER_OPERATOR_3: [ + { value: "|", type: "Text" }, + { value: "{{", type: "OpenExpression" }, + { value: "-1", type: "NumericLiteral" }, + { value: "|", type: "Pipe" }, + { value: "abs", type: "Identifier" }, + { value: "}}", type: "CloseExpression" }, + { value: "|", type: "Text" }, + { value: "{{", type: "OpenExpression" }, + { value: "1", type: "NumericLiteral" }, + { value: "|", type: "Pipe" }, + { value: "abs", type: "Identifier" }, + { value: "}}", type: "CloseExpression" }, + { value: "|", type: "Text" }, + ], // Logical operators between non-Booleans BOOLEAN_NUMERICAL: [ @@ -1518,6 +1584,8 @@ const TEST_CONTEXT = { FILTER_OPERATOR: { arr: [3, 2, 1], }, + FILTER_OPERATOR_2: {}, + FILTER_OPERATOR_3: {}, // Logical operators between non-Booleans BOOLEAN_NUMERICAL: {}, @@ -1594,6 +1662,8 @@ const EXPECTED_OUTPUTS = { // Filter operator FILTER_OPERATOR: `3451`, + FILTER_OPERATOR_2: `|3|ABCD|abcd|Test test|Test Test|a b|4|`, + FILTER_OPERATOR_3: `|1|1|`, // Logical operators between non-Booleans BOOLEAN_NUMERICAL: `|2|0|0|0|1|1|1|0|false|true|`,