Repository: jsonata-js/jsonata (js)

Issue:
$substring length parameter no longer works with negative start positions
I've just noticed that the `$substring()` function isn't handling its length argument correctly when the start argument is negative. For example, this expression correctly evaluates to `"world"`:

```
$substring("Hello world", -5, 5)
```

But so do all of these expressions:

```
$substring("Hello world", -5, 4)    // should return "worl"

$substring("Hello world", -5, 3)    // should return "wor"

$substring("Hello world", -5, 2)    // should return "wo"

$substring("Hello world", -5, 1)    // should return "w"

$substring("Hello world", -5, 0)    // should return ""

$substring("Hello world", -5, -1)   // should return ""
```

I'm guessing that this bug was introduced in the recent fix for [UTF-16 surrogate pairs](https://github.com/jsonata-js/jsonata/issues/156).

Pull request:
$substring should handle negative start position and length
The fix for #156 introduced a regression into the `$substring` function when dealing with negative values of start parameter (count from end of string).  This was caused by a miscalculation of the end position.  Also added checks to prevent start position and length from going below zero, in line with the previous behaviour.

Resolves #204 

Patch:
diff --git a/jsonata.js b/jsonata.js
index 1a28b40..22f758f 100644
--- a/jsonata.js
+++ b/jsonata.js
@@ -3106,8 +3106,17 @@ var jsonata = (function() {
         }
 
         var strArray = Array.from(str);
+        var strLength = strArray.length;
+
+        if(strLength + start < 0) {
+            start = 0;
+        }
+
         if(typeof length !== 'undefined') {
-            var end = start >= 0 ? start + length : strArray.length - start + length;
+            if(length <= 0) {
+                return '';
+            }
+            var end = start >= 0 ? start + length : strLength + start + length;
             return strArray.slice(start, end).join('');
         }
 


Test patch:
diff --git a/test/test-suite/groups/function-substring/case008.json b/test/test-suite/groups/function-substring/case008.json
new file mode 100644
index 0000000..1514752
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case008.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"hello world\", -5, 5)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": "world"
+}
\ No newline at end of file
diff --git a/test/test-suite/groups/function-substring/case009.json b/test/test-suite/groups/function-substring/case009.json
new file mode 100644
index 0000000..1d1759f
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case009.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"hello world\", -5, 4)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": "worl"
+}
\ No newline at end of file
diff --git a/test/test-suite/groups/function-substring/case010.json b/test/test-suite/groups/function-substring/case010.json
new file mode 100644
index 0000000..d65d64a
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case010.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"hello world\", -5, 1)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": "w"
+}
\ No newline at end of file
diff --git a/test/test-suite/groups/function-substring/case011.json b/test/test-suite/groups/function-substring/case011.json
new file mode 100644
index 0000000..e3b803b
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case011.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"hello world\", -5, 0)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": ""
+}
\ No newline at end of file
diff --git a/test/test-suite/groups/function-substring/case012.json b/test/test-suite/groups/function-substring/case012.json
new file mode 100644
index 0000000..a638b98
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case012.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"hello world\", -5, -1)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": ""
+}
\ No newline at end of file
diff --git a/test/test-suite/groups/function-substring/case013.json b/test/test-suite/groups/function-substring/case013.json
new file mode 100644
index 0000000..ac26559
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case013.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"hello world\", 0, -6)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": ""
+}
\ No newline at end of file
diff --git a/test/test-suite/groups/function-substring/case014.json b/test/test-suite/groups/function-substring/case014.json
new file mode 100644
index 0000000..f89e62c
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case014.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"hello world\", -100, 3)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": "hel"
+}
\ No newline at end of file
diff --git a/test/test-suite/groups/function-substring/case015.json b/test/test-suite/groups/function-substring/case015.json
new file mode 100644
index 0000000..7a6e5a3
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case015.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"😂😁😀\", -2, 1)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": "😁"
+}
\ No newline at end of file
diff --git a/test/test-suite/groups/function-substring/case016.json b/test/test-suite/groups/function-substring/case016.json
new file mode 100644
index 0000000..4d82a31
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case016.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"😂😁😀\", -3, 1)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": "😂"
+}
\ No newline at end of file
diff --git a/test/test-suite/groups/function-substring/case017.json b/test/test-suite/groups/function-substring/case017.json
new file mode 100644
index 0000000..c0f4fc5
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case017.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"😂😁😀\", -4, 1)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": "😂"
+}
\ No newline at end of file
diff --git a/test/test-suite/groups/function-substring/case018.json b/test/test-suite/groups/function-substring/case018.json
new file mode 100644
index 0000000..1e1ff57
--- /dev/null
+++ b/test/test-suite/groups/function-substring/case018.json
@@ -0,0 +1,6 @@
+{
+    "expr": "$substring(\"😂😁😀\", -5, 1)",
+    "dataset": "dataset5",
+    "bindings": {},
+    "result": "😂"
+}
\ No newline at end of file