We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Second expression in concatenated valueFrom is ignored
In the CommandLineTool:
```yaml
cwlVersion: v1.0
requirements:
  - class: InlineJavascriptRequirement
inputs:
  - id: parameter
    inputBinding:
      valueFrom: $("a ")$("string")
    type: string?
outputs: []
class: CommandLineTool
baseCommand: echo
```

The expression `$("a ")` is ignored on when this is run:

```
/usr/local/bin/cwltool 1.0.20180130110340
Resolved 'test.cwl' to 'file:///Users/th10/checkouts/arvados-pipeline/arvados-pipelines/cwl/test.cwl'
[job test.cwl] /private/var/folders/5p/p6z8hhy174q_ctt3pn7mx5_c000h1t/T/tmp8fqrl6ng$ echo \
    string
string
[job test.cwl] completed success
{}
Final process status is success
```

CWL version: 1.0.20180130110340

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cwltool/expression.py`
Content:
```
1 from __future__ import absolute_import
2 import copy
3 import json
4 import logging
5 import re
6 from typing import Any, AnyStr, Dict, List, Text, Union
7 from .utils import docker_windows_path_adjust
8 import six
9 from six import u
10 
11 from . import sandboxjs
12 from .errors import WorkflowException
13 from .utils import bytes2str_in_dicts
14 
15 _logger = logging.getLogger("cwltool")
16 
17 
18 def jshead(engineConfig, rootvars):
19     # type: (List[Text], Dict[Text, Any]) -> Text
20 
21     # make sure all the byte strings are converted
22     # to str in `rootvars` dict.
23     # TODO: need to make sure the `rootvars dict`
24     # contains no bytes type in the first place.
25     if six.PY3:
26         rootvars = bytes2str_in_dicts(rootvars)  # type: ignore
27 
28     return u"\n".join(engineConfig + [u"var %s = %s;" % (k, json.dumps(v, indent=4)) for k, v in rootvars.items()])
29 
30 
31 # decode all raw strings to unicode
32 seg_symbol = r"""\w+"""
33 seg_single = r"""\['([^']|\\')+'\]"""
34 seg_double = r"""\["([^"]|\\")+"\]"""
35 seg_index = r"""\[[0-9]+\]"""
36 segments = r"(\.%s|%s|%s|%s)" % (seg_symbol, seg_single, seg_double, seg_index)
37 segment_re = re.compile(u(segments), flags=re.UNICODE)
38 param_str = r"\((%s)%s*\)$" % (seg_symbol, segments)
39 param_re = re.compile(u(param_str), flags=re.UNICODE)
40 
41 JSON = Union[Dict[Any, Any], List[Any], Text, int, float, bool, None]
42 
43 
44 class SubstitutionError(Exception):
45     pass
46 
47 
48 def scanner(scan):  # type: (Text) -> List[int]
49     DEFAULT = 0
50     DOLLAR = 1
51     PAREN = 2
52     BRACE = 3
53     SINGLE_QUOTE = 4
54     DOUBLE_QUOTE = 5
55     BACKSLASH = 6
56 
57     i = 0
58     stack = [DEFAULT]
59     start = 0
60     while i < len(scan):
61         state = stack[-1]
62         c = scan[i]
63 
64         if state == DEFAULT:
65             if c == '$':
66                 stack.append(DOLLAR)
67             elif c == '\\':
68                 stack.append(BACKSLASH)
69         elif state == BACKSLASH:
70             stack.pop()
71             if stack[-1] == DEFAULT:
72                 return [i - 1, i + 1]
73         elif state == DOLLAR:
74             if c == '(':
75                 start = i - 1
76                 stack.append(PAREN)
77             elif c == '{':
78                 start = i - 1
79                 stack.append(BRACE)
80             else:
81                 stack.pop()
82         elif state == PAREN:
83             if c == '(':
84                 stack.append(PAREN)
85             elif c == ')':
86                 stack.pop()
87                 if stack[-1] == DOLLAR:
88                     return [start, i + 1]
89             elif c == "'":
90                 stack.append(SINGLE_QUOTE)
91             elif c == '"':
92                 stack.append(DOUBLE_QUOTE)
93         elif state == BRACE:
94             if c == '{':
95                 stack.append(BRACE)
96             elif c == '}':
97                 stack.pop()
98                 if stack[-1] == DOLLAR:
99                     return [start, i + 1]
100             elif c == "'":
101                 stack.append(SINGLE_QUOTE)
102             elif c == '"':
103                 stack.append(DOUBLE_QUOTE)
104         elif state == SINGLE_QUOTE:
105             if c == "'":
106                 stack.pop()
107             elif c == '\\':
108                 stack.append(BACKSLASH)
109         elif state == DOUBLE_QUOTE:
110             if c == '"':
111                 stack.pop()
112             elif c == '\\':
113                 stack.append(BACKSLASH)
114         i += 1
115 
116     if len(stack) > 1:
117         raise SubstitutionError(
118             "Substitution error, unfinished block starting at position {}: {}".format(start, scan[start:]))
119     else:
120         return None
121 
122 
123 def next_seg(remain, obj):  # type: (Text, Any) -> Any
124     if remain:
125         m = segment_re.match(remain)
126         key = None  # type: Union[Text, int]
127         if m.group(0)[0] == '.':
128             key = m.group(0)[1:]
129         elif m.group(0)[1] in ("'", '"'):
130             key = m.group(0)[2:-2].replace("\\'", "'").replace('\\"', '"')
131 
132         if key:
133             if isinstance(obj, list) and key == "length" and not remain[m.end(0):]:
134                 return len(obj)
135             if not isinstance(obj, dict):
136                 raise WorkflowException(" is a %s, cannot index on string '%s'" % (type(obj).__name__, key))
137             if key not in obj:
138                 raise WorkflowException(" does not contain key '%s'" % key)
139         else:
140             try:
141                 key = int(m.group(0)[1:-1])
142             except ValueError as v:
143                 raise WorkflowException(u(str(v)))
144             if not isinstance(obj, list):
145                 raise WorkflowException(" is a %s, cannot index on int '%s'" % (type(obj).__name__, key))
146             if key >= len(obj):
147                 raise WorkflowException(" list index %i out of range" % key)
148         try:
149             return next_seg(remain[m.end(0):], obj[key])
150         except WorkflowException as w:
151             raise WorkflowException("%s%s" % (m.group(0), w))
152     else:
153         return obj
154 
155 
156 def evaluator(ex, jslib, obj, fullJS=False, timeout=None, force_docker_pull=False, debug=False, js_console=False):
157     # type: (Text, Text, Dict[Text, Any], bool, int, bool, bool, bool) -> JSON
158     m = param_re.match(ex)
159     if m:
160         if m.end(1)+1 == len(ex) and m.group(1) == "null":
161             return None
162         try:
163             return next_seg(m.group(0)[m.end(1) - m.start(0):-1], obj[m.group(1)])
164         except Exception as w:
165             raise WorkflowException("%s%s" % (m.group(1), w))
166     elif fullJS:
167         return sandboxjs.execjs(ex, jslib, timeout=timeout, force_docker_pull=force_docker_pull, debug=debug, js_console=js_console)
168     else:
169         raise sandboxjs.JavascriptException(
170             "Syntax error in parameter reference '%s' or used Javascript code without specifying InlineJavascriptRequirement.",
171             ex)
172 
173 
174 def interpolate(scan, rootvars,
175                 timeout=None, fullJS=None, jslib="", force_docker_pull=False,
176                 debug=False, js_console=False):
177     # type: (Text, Dict[Text, Any], int, bool, Union[str, Text], bool, bool, bool) -> JSON
178     scan = scan.strip()
179     parts = []
180     w = scanner(scan)
181     while w:
182         parts.append(scan[0:w[0]])
183 
184         if scan[w[0]] == '$':
185             e = evaluator(scan[w[0] + 1:w[1]], jslib, rootvars, fullJS=fullJS,
186                           timeout=timeout, force_docker_pull=force_docker_pull,
187                           debug=debug, js_console=js_console)
188             if w[0] == 0 and w[1] == len(scan):
189                 return e
190             leaf = json.dumps(e, sort_keys=True)
191             if leaf[0] == '"':
192                 leaf = leaf[1:-1]
193             parts.append(leaf)
194         elif scan[w[0]] == '\\':
195             e = scan[w[1] - 1]
196             parts.append(e)
197 
198         scan = scan[w[1]:]
199         w = scanner(scan)
200     parts.append(scan)
201     return ''.join(parts)
202 
203 
204 def do_eval(ex, jobinput, requirements, outdir, tmpdir, resources,
205             context=None, pull_image=True, timeout=None, force_docker_pull=False, debug=False, js_console=False):
206     # type: (Union[dict, AnyStr], Dict[Text, Union[Dict, List, Text]], List[Dict[Text, Any]], Text, Text, Dict[Text, Union[int, Text]], Any, bool, int, bool, bool, bool) -> Any
207 
208     runtime = copy.copy(resources)
209     runtime["tmpdir"] = docker_windows_path_adjust(tmpdir)
210     runtime["outdir"] = docker_windows_path_adjust(outdir)
211 
212     rootvars = {
213         u"inputs": jobinput,
214         u"self": context,
215         u"runtime": runtime}
216 
217     if isinstance(ex, (str, Text)) and ("$(" in ex or "${" in ex):
218         fullJS = False
219         jslib = u""
220         for r in reversed(requirements):
221             if r["class"] == "InlineJavascriptRequirement":
222                 fullJS = True
223                 jslib = jshead(r.get("expressionLib", []), rootvars)
224                 break
225 
226         try:
227             return interpolate(ex,
228                                rootvars,
229                                timeout=timeout,
230                                fullJS=fullJS,
231                                jslib=jslib,
232                                force_docker_pull=force_docker_pull,
233                                debug=debug,
234                                js_console=js_console)
235 
236         except Exception as e:
237             raise WorkflowException("Expression evaluation error:\n%s" % e)
238     else:
239         return ex
240
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/cwltool/expression.py b/cwltool/expression.py
--- a/cwltool/expression.py
+++ b/cwltool/expression.py
@@ -185,7 +185,7 @@
             e = evaluator(scan[w[0] + 1:w[1]], jslib, rootvars, fullJS=fullJS,
                           timeout=timeout, force_docker_pull=force_docker_pull,
                           debug=debug, js_console=js_console)
-            if w[0] == 0 and w[1] == len(scan):
+            if w[0] == 0 and w[1] == len(scan) and len(parts) <= 1:
                 return e
             leaf = json.dumps(e, sort_keys=True)
             if leaf[0] == '"':

{"golden_diff": "diff --git a/cwltool/expression.py b/cwltool/expression.py\n--- a/cwltool/expression.py\n+++ b/cwltool/expression.py\n@@ -185,7 +185,7 @@\n             e = evaluator(scan[w[0] + 1:w[1]], jslib, rootvars, fullJS=fullJS,\n                           timeout=timeout, force_docker_pull=force_docker_pull,\n                           debug=debug, js_console=js_console)\n-            if w[0] == 0 and w[1] == len(scan):\n+            if w[0] == 0 and w[1] == len(scan) and len(parts) <= 1:\n                 return e\n             leaf = json.dumps(e, sort_keys=True)\n             if leaf[0] == '\"':\n", "issue": "Second expression in concatenated valueFrom is ignored\nIn the CommandLineTool:\r\n```yaml\r\ncwlVersion: v1.0\r\nrequirements:\r\n  - class: InlineJavascriptRequirement\r\ninputs:\r\n  - id: parameter\r\n    inputBinding:\r\n      valueFrom: $(\"a \")$(\"string\")\r\n    type: string?\r\noutputs: []\r\nclass: CommandLineTool\r\nbaseCommand: echo\r\n```\r\n\r\nThe expression `$(\"a \")` is ignored on when this is run:\r\n\r\n```\r\n/usr/local/bin/cwltool 1.0.20180130110340\r\nResolved 'test.cwl' to 'file:///Users/th10/checkouts/arvados-pipeline/arvados-pipelines/cwl/test.cwl'\r\n[job test.cwl] /private/var/folders/5p/p6z8hhy174q_ctt3pn7mx5_c000h1t/T/tmp8fqrl6ng$ echo \\\r\n    string\r\nstring\r\n[job test.cwl] completed success\r\n{}\r\nFinal process status is success\r\n```\r\n\r\nCWL version: 1.0.20180130110340\n", "before_files": [{"content": "from __future__ import absolute_import\nimport copy\nimport json\nimport logging\nimport re\nfrom typing import Any, AnyStr, Dict, List, Text, Union\nfrom .utils import docker_windows_path_adjust\nimport six\nfrom six import u\n\nfrom . import sandboxjs\nfrom .errors import WorkflowException\nfrom .utils import bytes2str_in_dicts\n\n_logger = logging.getLogger(\"cwltool\")\n\n\ndef jshead(engineConfig, rootvars):\n    # type: (List[Text], Dict[Text, Any]) -> Text\n\n    # make sure all the byte strings are converted\n    # to str in `rootvars` dict.\n    # TODO: need to make sure the `rootvars dict`\n    # contains no bytes type in the first place.\n    if six.PY3:\n        rootvars = bytes2str_in_dicts(rootvars)  # type: ignore\n\n    return u\"\\n\".join(engineConfig + [u\"var %s = %s;\" % (k, json.dumps(v, indent=4)) for k, v in rootvars.items()])\n\n\n# decode all raw strings to unicode\nseg_symbol = r\"\"\"\\w+\"\"\"\nseg_single = r\"\"\"\\['([^']|\\\\')+'\\]\"\"\"\nseg_double = r\"\"\"\\[\"([^\"]|\\\\\")+\"\\]\"\"\"\nseg_index = r\"\"\"\\[[0-9]+\\]\"\"\"\nsegments = r\"(\\.%s|%s|%s|%s)\" % (seg_symbol, seg_single, seg_double, seg_index)\nsegment_re = re.compile(u(segments), flags=re.UNICODE)\nparam_str = r\"\\((%s)%s*\\)$\" % (seg_symbol, segments)\nparam_re = re.compile(u(param_str), flags=re.UNICODE)\n\nJSON = Union[Dict[Any, Any], List[Any], Text, int, float, bool, None]\n\n\nclass SubstitutionError(Exception):\n    pass\n\n\ndef scanner(scan):  # type: (Text) -> List[int]\n    DEFAULT = 0\n    DOLLAR = 1\n    PAREN = 2\n    BRACE = 3\n    SINGLE_QUOTE = 4\n    DOUBLE_QUOTE = 5\n    BACKSLASH = 6\n\n    i = 0\n    stack = [DEFAULT]\n    start = 0\n    while i < len(scan):\n        state = stack[-1]\n        c = scan[i]\n\n        if state == DEFAULT:\n            if c == '$':\n                stack.append(DOLLAR)\n            elif c == '\\\\':\n                stack.append(BACKSLASH)\n        elif state == BACKSLASH:\n            stack.pop()\n            if stack[-1] == DEFAULT:\n                return [i - 1, i + 1]\n        elif state == DOLLAR:\n            if c == '(':\n                start = i - 1\n                stack.append(PAREN)\n            elif c == '{':\n                start = i - 1\n                stack.append(BRACE)\n            else:\n                stack.pop()\n        elif state == PAREN:\n            if c == '(':\n                stack.append(PAREN)\n            elif c == ')':\n                stack.pop()\n                if stack[-1] == DOLLAR:\n                    return [start, i + 1]\n            elif c == \"'\":\n                stack.append(SINGLE_QUOTE)\n            elif c == '\"':\n                stack.append(DOUBLE_QUOTE)\n        elif state == BRACE:\n            if c == '{':\n                stack.append(BRACE)\n            elif c == '}':\n                stack.pop()\n                if stack[-1] == DOLLAR:\n                    return [start, i + 1]\n            elif c == \"'\":\n                stack.append(SINGLE_QUOTE)\n            elif c == '\"':\n                stack.append(DOUBLE_QUOTE)\n        elif state == SINGLE_QUOTE:\n            if c == \"'\":\n                stack.pop()\n            elif c == '\\\\':\n                stack.append(BACKSLASH)\n        elif state == DOUBLE_QUOTE:\n            if c == '\"':\n                stack.pop()\n            elif c == '\\\\':\n                stack.append(BACKSLASH)\n        i += 1\n\n    if len(stack) > 1:\n        raise SubstitutionError(\n            \"Substitution error, unfinished block starting at position {}: {}\".format(start, scan[start:]))\n    else:\n        return None\n\n\ndef next_seg(remain, obj):  # type: (Text, Any) -> Any\n    if remain:\n        m = segment_re.match(remain)\n        key = None  # type: Union[Text, int]\n        if m.group(0)[0] == '.':\n            key = m.group(0)[1:]\n        elif m.group(0)[1] in (\"'\", '\"'):\n            key = m.group(0)[2:-2].replace(\"\\\\'\", \"'\").replace('\\\\\"', '\"')\n\n        if key:\n            if isinstance(obj, list) and key == \"length\" and not remain[m.end(0):]:\n                return len(obj)\n            if not isinstance(obj, dict):\n                raise WorkflowException(\" is a %s, cannot index on string '%s'\" % (type(obj).__name__, key))\n            if key not in obj:\n                raise WorkflowException(\" does not contain key '%s'\" % key)\n        else:\n            try:\n                key = int(m.group(0)[1:-1])\n            except ValueError as v:\n                raise WorkflowException(u(str(v)))\n            if not isinstance(obj, list):\n                raise WorkflowException(\" is a %s, cannot index on int '%s'\" % (type(obj).__name__, key))\n            if key >= len(obj):\n                raise WorkflowException(\" list index %i out of range\" % key)\n        try:\n            return next_seg(remain[m.end(0):], obj[key])\n        except WorkflowException as w:\n            raise WorkflowException(\"%s%s\" % (m.group(0), w))\n    else:\n        return obj\n\n\ndef evaluator(ex, jslib, obj, fullJS=False, timeout=None, force_docker_pull=False, debug=False, js_console=False):\n    # type: (Text, Text, Dict[Text, Any], bool, int, bool, bool, bool) -> JSON\n    m = param_re.match(ex)\n    if m:\n        if m.end(1)+1 == len(ex) and m.group(1) == \"null\":\n            return None\n        try:\n            return next_seg(m.group(0)[m.end(1) - m.start(0):-1], obj[m.group(1)])\n        except Exception as w:\n            raise WorkflowException(\"%s%s\" % (m.group(1), w))\n    elif fullJS:\n        return sandboxjs.execjs(ex, jslib, timeout=timeout, force_docker_pull=force_docker_pull, debug=debug, js_console=js_console)\n    else:\n        raise sandboxjs.JavascriptException(\n            \"Syntax error in parameter reference '%s' or used Javascript code without specifying InlineJavascriptRequirement.\",\n            ex)\n\n\ndef interpolate(scan, rootvars,\n                timeout=None, fullJS=None, jslib=\"\", force_docker_pull=False,\n                debug=False, js_console=False):\n    # type: (Text, Dict[Text, Any], int, bool, Union[str, Text], bool, bool, bool) -> JSON\n    scan = scan.strip()\n    parts = []\n    w = scanner(scan)\n    while w:\n        parts.append(scan[0:w[0]])\n\n        if scan[w[0]] == '$':\n            e = evaluator(scan[w[0] + 1:w[1]], jslib, rootvars, fullJS=fullJS,\n                          timeout=timeout, force_docker_pull=force_docker_pull,\n                          debug=debug, js_console=js_console)\n            if w[0] == 0 and w[1] == len(scan):\n                return e\n            leaf = json.dumps(e, sort_keys=True)\n            if leaf[0] == '\"':\n                leaf = leaf[1:-1]\n            parts.append(leaf)\n        elif scan[w[0]] == '\\\\':\n            e = scan[w[1] - 1]\n            parts.append(e)\n\n        scan = scan[w[1]:]\n        w = scanner(scan)\n    parts.append(scan)\n    return ''.join(parts)\n\n\ndef do_eval(ex, jobinput, requirements, outdir, tmpdir, resources,\n            context=None, pull_image=True, timeout=None, force_docker_pull=False, debug=False, js_console=False):\n    # type: (Union[dict, AnyStr], Dict[Text, Union[Dict, List, Text]], List[Dict[Text, Any]], Text, Text, Dict[Text, Union[int, Text]], Any, bool, int, bool, bool, bool) -> Any\n\n    runtime = copy.copy(resources)\n    runtime[\"tmpdir\"] = docker_windows_path_adjust(tmpdir)\n    runtime[\"outdir\"] = docker_windows_path_adjust(outdir)\n\n    rootvars = {\n        u\"inputs\": jobinput,\n        u\"self\": context,\n        u\"runtime\": runtime}\n\n    if isinstance(ex, (str, Text)) and (\"$(\" in ex or \"${\" in ex):\n        fullJS = False\n        jslib = u\"\"\n        for r in reversed(requirements):\n            if r[\"class\"] == \"InlineJavascriptRequirement\":\n                fullJS = True\n                jslib = jshead(r.get(\"expressionLib\", []), rootvars)\n                break\n\n        try:\n            return interpolate(ex,\n                               rootvars,\n                               timeout=timeout,\n                               fullJS=fullJS,\n                               jslib=jslib,\n                               force_docker_pull=force_docker_pull,\n                               debug=debug,\n                               js_console=js_console)\n\n        except Exception as e:\n            raise WorkflowException(\"Expression evaluation error:\\n%s\" % e)\n    else:\n        return ex\n", "path": "cwltool/expression.py"}], "after_files": [{"content": "from __future__ import absolute_import\nimport copy\nimport json\nimport logging\nimport re\nfrom typing import Any, AnyStr, Dict, List, Text, Union\nfrom .utils import docker_windows_path_adjust\nimport six\nfrom six import u\n\nfrom . import sandboxjs\nfrom .errors import WorkflowException\nfrom .utils import bytes2str_in_dicts\n\n_logger = logging.getLogger(\"cwltool\")\n\n\ndef jshead(engineConfig, rootvars):\n    # type: (List[Text], Dict[Text, Any]) -> Text\n\n    # make sure all the byte strings are converted\n    # to str in `rootvars` dict.\n    # TODO: need to make sure the `rootvars dict`\n    # contains no bytes type in the first place.\n    if six.PY3:\n        rootvars = bytes2str_in_dicts(rootvars)  # type: ignore\n\n    return u\"\\n\".join(engineConfig + [u\"var %s = %s;\" % (k, json.dumps(v, indent=4)) for k, v in rootvars.items()])\n\n\n# decode all raw strings to unicode\nseg_symbol = r\"\"\"\\w+\"\"\"\nseg_single = r\"\"\"\\['([^']|\\\\')+'\\]\"\"\"\nseg_double = r\"\"\"\\[\"([^\"]|\\\\\")+\"\\]\"\"\"\nseg_index = r\"\"\"\\[[0-9]+\\]\"\"\"\nsegments = r\"(\\.%s|%s|%s|%s)\" % (seg_symbol, seg_single, seg_double, seg_index)\nsegment_re = re.compile(u(segments), flags=re.UNICODE)\nparam_str = r\"\\((%s)%s*\\)$\" % (seg_symbol, segments)\nparam_re = re.compile(u(param_str), flags=re.UNICODE)\n\nJSON = Union[Dict[Any, Any], List[Any], Text, int, float, bool, None]\n\n\nclass SubstitutionError(Exception):\n    pass\n\n\ndef scanner(scan):  # type: (Text) -> List[int]\n    DEFAULT = 0\n    DOLLAR = 1\n    PAREN = 2\n    BRACE = 3\n    SINGLE_QUOTE = 4\n    DOUBLE_QUOTE = 5\n    BACKSLASH = 6\n\n    i = 0\n    stack = [DEFAULT]\n    start = 0\n    while i < len(scan):\n        state = stack[-1]\n        c = scan[i]\n\n        if state == DEFAULT:\n            if c == '$':\n                stack.append(DOLLAR)\n            elif c == '\\\\':\n                stack.append(BACKSLASH)\n        elif state == BACKSLASH:\n            stack.pop()\n            if stack[-1] == DEFAULT:\n                return [i - 1, i + 1]\n        elif state == DOLLAR:\n            if c == '(':\n                start = i - 1\n                stack.append(PAREN)\n            elif c == '{':\n                start = i - 1\n                stack.append(BRACE)\n            else:\n                stack.pop()\n        elif state == PAREN:\n            if c == '(':\n                stack.append(PAREN)\n            elif c == ')':\n                stack.pop()\n                if stack[-1] == DOLLAR:\n                    return [start, i + 1]\n            elif c == \"'\":\n                stack.append(SINGLE_QUOTE)\n            elif c == '\"':\n                stack.append(DOUBLE_QUOTE)\n        elif state == BRACE:\n            if c == '{':\n                stack.append(BRACE)\n            elif c == '}':\n                stack.pop()\n                if stack[-1] == DOLLAR:\n                    return [start, i + 1]\n            elif c == \"'\":\n                stack.append(SINGLE_QUOTE)\n            elif c == '\"':\n                stack.append(DOUBLE_QUOTE)\n        elif state == SINGLE_QUOTE:\n            if c == \"'\":\n                stack.pop()\n            elif c == '\\\\':\n                stack.append(BACKSLASH)\n        elif state == DOUBLE_QUOTE:\n            if c == '\"':\n                stack.pop()\n            elif c == '\\\\':\n                stack.append(BACKSLASH)\n        i += 1\n\n    if len(stack) > 1:\n        raise SubstitutionError(\n            \"Substitution error, unfinished block starting at position {}: {}\".format(start, scan[start:]))\n    else:\n        return None\n\n\ndef next_seg(remain, obj):  # type: (Text, Any) -> Any\n    if remain:\n        m = segment_re.match(remain)\n        key = None  # type: Union[Text, int]\n        if m.group(0)[0] == '.':\n            key = m.group(0)[1:]\n        elif m.group(0)[1] in (\"'\", '\"'):\n            key = m.group(0)[2:-2].replace(\"\\\\'\", \"'\").replace('\\\\\"', '\"')\n\n        if key:\n            if isinstance(obj, list) and key == \"length\" and not remain[m.end(0):]:\n                return len(obj)\n            if not isinstance(obj, dict):\n                raise WorkflowException(\" is a %s, cannot index on string '%s'\" % (type(obj).__name__, key))\n            if key not in obj:\n                raise WorkflowException(\" does not contain key '%s'\" % key)\n        else:\n            try:\n                key = int(m.group(0)[1:-1])\n            except ValueError as v:\n                raise WorkflowException(u(str(v)))\n            if not isinstance(obj, list):\n                raise WorkflowException(\" is a %s, cannot index on int '%s'\" % (type(obj).__name__, key))\n            if key >= len(obj):\n                raise WorkflowException(\" list index %i out of range\" % key)\n        try:\n            return next_seg(remain[m.end(0):], obj[key])\n        except WorkflowException as w:\n            raise WorkflowException(\"%s%s\" % (m.group(0), w))\n    else:\n        return obj\n\n\ndef evaluator(ex, jslib, obj, fullJS=False, timeout=None, force_docker_pull=False, debug=False, js_console=False):\n    # type: (Text, Text, Dict[Text, Any], bool, int, bool, bool, bool) -> JSON\n    m = param_re.match(ex)\n    if m:\n        if m.end(1)+1 == len(ex) and m.group(1) == \"null\":\n            return None\n        try:\n            return next_seg(m.group(0)[m.end(1) - m.start(0):-1], obj[m.group(1)])\n        except Exception as w:\n            raise WorkflowException(\"%s%s\" % (m.group(1), w))\n    elif fullJS:\n        return sandboxjs.execjs(ex, jslib, timeout=timeout, force_docker_pull=force_docker_pull, debug=debug, js_console=js_console)\n    else:\n        raise sandboxjs.JavascriptException(\n            \"Syntax error in parameter reference '%s' or used Javascript code without specifying InlineJavascriptRequirement.\",\n            ex)\n\n\ndef interpolate(scan, rootvars,\n                timeout=None, fullJS=None, jslib=\"\", force_docker_pull=False,\n                debug=False, js_console=False):\n    # type: (Text, Dict[Text, Any], int, bool, Union[str, Text], bool, bool, bool) -> JSON\n    scan = scan.strip()\n    parts = []\n    w = scanner(scan)\n    while w:\n        parts.append(scan[0:w[0]])\n\n        if scan[w[0]] == '$':\n            e = evaluator(scan[w[0] + 1:w[1]], jslib, rootvars, fullJS=fullJS,\n                          timeout=timeout, force_docker_pull=force_docker_pull,\n                          debug=debug, js_console=js_console)\n            if w[0] == 0 and w[1] == len(scan) and len(parts) <= 1:\n                return e\n            leaf = json.dumps(e, sort_keys=True)\n            if leaf[0] == '\"':\n                leaf = leaf[1:-1]\n            parts.append(leaf)\n        elif scan[w[0]] == '\\\\':\n            e = scan[w[1] - 1]\n            parts.append(e)\n\n        scan = scan[w[1]:]\n        w = scanner(scan)\n    parts.append(scan)\n    return ''.join(parts)\n\n\ndef do_eval(ex, jobinput, requirements, outdir, tmpdir, resources,\n            context=None, pull_image=True, timeout=None, force_docker_pull=False, debug=False, js_console=False):\n    # type: (Union[dict, AnyStr], Dict[Text, Union[Dict, List, Text]], List[Dict[Text, Any]], Text, Text, Dict[Text, Union[int, Text]], Any, bool, int, bool, bool, bool) -> Any\n\n    runtime = copy.copy(resources)\n    runtime[\"tmpdir\"] = docker_windows_path_adjust(tmpdir)\n    runtime[\"outdir\"] = docker_windows_path_adjust(outdir)\n\n    rootvars = {\n        u\"inputs\": jobinput,\n        u\"self\": context,\n        u\"runtime\": runtime}\n\n    if isinstance(ex, (str, Text)) and (\"$(\" in ex or \"${\" in ex):\n        fullJS = False\n        jslib = u\"\"\n        for r in reversed(requirements):\n            if r[\"class\"] == \"InlineJavascriptRequirement\":\n                fullJS = True\n                jslib = jshead(r.get(\"expressionLib\", []), rootvars)\n                break\n\n        try:\n            return interpolate(ex,\n                               rootvars,\n                               timeout=timeout,\n                               fullJS=fullJS,\n                               jslib=jslib,\n                               force_docker_pull=force_docker_pull,\n                               debug=debug,\n                               js_console=js_console)\n\n        except Exception as e:\n            raise WorkflowException(\"Expression evaluation error:\\n%s\" % e)\n    else:\n        return ex\n", "path": "cwltool/expression.py"}]}