From 85b98f1111855bb8a1a096a6ed02a6b6e33e5ad1 Mon Sep 17 00:00:00 2001 From: DBHeise Date: Fri, 12 Apr 2019 18:44:26 +0000 Subject: [PATCH] adding BraceMatching operation --- src/core/config/Categories.json | 3 +- src/core/operations/Bracematching.mjs | 102 +++++++++++++++++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/BraceMatching.mjs | 101 ++++++++++++++++++++++ 4 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 src/core/operations/Bracematching.mjs create mode 100644 tests/operations/tests/BraceMatching.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 2d194c37..2a0180a3 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -230,7 +230,8 @@ "Escape string", "Unescape string", "Pseudo-Random Number Generator", - "Sleep" + "Sleep", + "Brace Matching" ] }, { diff --git a/src/core/operations/Bracematching.mjs b/src/core/operations/Bracematching.mjs new file mode 100644 index 00000000..3758e2a6 --- /dev/null +++ b/src/core/operations/Bracematching.mjs @@ -0,0 +1,102 @@ +/** + * @author DBHeise [david@heiseink.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +//import OperationError from "../errors/OperationError"; + +/** + * Brace Matching operation + */ +class BraceMatching extends Operation { + + /** + * BraceMatching constructor + */ + constructor() { + super(); + + this.name = "Brace Matching"; + this.module = "Default"; + this.description = "Extracts nested grouping"; + this.infoURL = "https://wikipedia.org/wiki/Brace_matching"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Open Brace Character", + type: "string", + value: "(" + }, + { + name: "Close Brace Character", + type: "string", + value: ")" + }, + { + name: "String Chars", + type: "string", + value: "\"'" + }, + { + name: "Escape Char", + type: "string", + value: "\\" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [openChar, closeChar, strChars, escChar] = args; + let ans = ""; + let isInString = false; + let nestLevel = 0; + let stringChar = null; + for (let i = 0; i < input.length; i++) { + const ch = input[i]; + if (ch === openChar) { + if (!isInString) { + nestLevel++; + if (nestLevel === 1) { + continue; + } + } + } else if (ch === closeChar) { + if (!isInString) { + nestLevel--; + if (nestLevel < 1) { + break; + } + } + } + if (ch === escChar) { + ans += ch; + ans += input[i + 1]; + i++; + continue; + } else if (stringChar && ch === stringChar) { + isInString = false; + stringChar = null; + } else if (strChars.indexOf(ch) > -1) { + if (!isInString) { + isInString = true; + stringChar = ch; + } + } + if (nestLevel > 0) { + ans += ch; + } + } + return ans; + } +} + +export default BraceMatching; + diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 41d78c35..7eed6e7e 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -90,6 +90,7 @@ import "./tests/Typex"; import "./tests/BLAKE2b"; import "./tests/BLAKE2s"; import "./tests/Protobuf"; +import "./tests/BraceMatching"; // Cannot test operations that use the File type yet //import "./tests/SplitColourChannels"; diff --git a/tests/operations/tests/BraceMatching.mjs b/tests/operations/tests/BraceMatching.mjs new file mode 100644 index 00000000..8f6f198d --- /dev/null +++ b/tests/operations/tests/BraceMatching.mjs @@ -0,0 +1,101 @@ +/** + * Brace Matching tests. + * + * @author DBHeise [david@heiseink.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import TestRegister from "../TestRegister"; + +TestRegister.addTests([ + { + name: "Brace Matching Simple", + input: "(test)", + expectedOutput: "test", + recipeConfig: [ + { + "op": "Brace Matching", + "args": ["(", ")", "\"'", "\\"] + } + ] + }, + { + name: "Brace Matching Simple with extra text", + input: "this is a simple (test) of this function", + expectedOutput: "test", + recipeConfig: [ + { + "op": "Brace Matching", + "args": ["(", ")", "\"'", "\\"] + } + ] + }, + { + name: "Brace Matching Simple with strings", + input: "{test \"}\"}", + expectedOutput: "test \"}\"", + recipeConfig: [ + { + "op": "Brace Matching", + "args": ["{", "}", "\"'", "\\"] + } + ] + }, + { + name: "Brace Matching Simple with strings 2", + input: "{test '}'}", + expectedOutput: "test '}'", + recipeConfig: [ + { + "op": "Brace Matching", + "args": ["{", "}", "\"'", "\\"] + } + ] + }, + { + name: "Brace Matching Simple nested", + input: "[this [test] good]", + expectedOutput: "this [test] good", + recipeConfig: [ + { + "op": "Brace Matching", + "args": ["[", "]", "\"'", "\\"] + } + ] + }, + { + name: "Brace Matching Simple escaped", + input: " foo bar>", + expectedOutput: "test \\> foo bar", + recipeConfig: [ + { + "op": "Brace Matching", + "args": ["<", ">", "\"'", "\\"] + } + ] + }, + { + name: "Brace Matching Complex multi nesting 1", + input: "(((((test)))))(((((test)))))", + expectedOutput: "((((test))))", + recipeConfig: [ + { + "op": "Brace Matching", + "args": ["(", ")", "\"'", "\\"] + } + ] + }, + { + name: "Brace Matching Complex multi nesting 2", + input: "(((((test))))(((((test))))))", + expectedOutput: "((((test))))(((((test)))))", + recipeConfig: [ + { + "op": "Brace Matching", + "args": ["(", ")", "\"'", "\\"] + } + ] + } +]); +