about summary refs log tree commit diff
diff options
context:
space:
mode:
authorArun Isaac2026-01-28 19:09:10 +0000
committerArun Isaac2026-01-28 21:02:19 +0000
commitd980fcd1a5fdbe0fc5e1833c7366903d4b0c3685 (patch)
tree2fdd0012c06af04719956721d682abbdcd1ceb6a
parent55d04bbe46581d6e0f3da97f7b9339ccf5738d21 (diff)
downloadravanan-d980fcd1a5fdbe0fc5e1833c7366903d4b0c3685.tar.gz
ravanan-d980fcd1a5fdbe0fc5e1833c7366903d4b0c3685.tar.lz
ravanan-d980fcd1a5fdbe0fc5e1833c7366903d4b0c3685.zip
javascript: Allow whitespace within javascript string interpolation.
We now trim procedurally instead of relying on the PEG grammar. It's
simpler, and it actually works.
-rw-r--r--ravanan/javascript.scm96
-rw-r--r--tests/javascript.scm18
2 files changed, 77 insertions, 37 deletions
diff --git a/ravanan/javascript.scm b/ravanan/javascript.scm
index 474c01c..1523d25 100644
--- a/ravanan/javascript.scm
+++ b/ravanan/javascript.scm
@@ -1,5 +1,5 @@
 ;;; ravanan --- High-reproducibility CWL runner powered by Guix
-;;; Copyright © 2024–2025 Arun Isaac <arunisaac@systemreboot.net>
+;;; Copyright © 2024–2026 Arun Isaac <arunisaac@systemreboot.net>
 ;;;
 ;;; This file is part of ravanan.
 ;;;
@@ -95,21 +95,15 @@
 (define-peg-pattern javascript-function-body all
   (and (ignore "$") javascript-function-body-subexpression))
 
-(define-peg-pattern whitespace body
-  (or "\t" "\n" "\r" " "))
-
 (define-peg-pattern string-literal body
-  (+ (and (not-followed-by (or "$(" whitespace))
+  (+ (and (not-followed-by "$(")
           peg-any)))
 
 (define-peg-pattern javascript all
-  (and (ignore (* whitespace))
-       (* (and (* whitespace)
-               (or parameter-reference
-                   javascript-expression
-                   javascript-function-body
-                   string-literal)))
-       (ignore (* whitespace))))
+  (* (or parameter-reference
+         javascript-expression
+         javascript-function-body
+         string-literal)))
 
 (define (javascript-expression? str)
   "Return true value if @var{str} contains inline javascript or parameter
@@ -213,30 +207,60 @@ keys @code{\"inputs\"}, @code{\"self\"} and @code{\"runtime\"}.
 
 @var{expression-lib} is a list of expressions evaluated before evaluating
 @var{expression}."
-  (match (peg:tree (match-pattern javascript str))
-    ;; There is only one expression. This is not a string interpolation. Do not
-    ;; serialize JSON.
-    (('javascript expression-tree)
-     (evaluate-expression-tree-1 expression-tree
-                                 context
-                                 expression-lib))
-    ;; This is a string interpolation. Evaluate expressions and serialize JSON.
-    (('javascript expression-trees ...)
-     (let ((vals (map (cut evaluate-expression-tree-1 <> context expression-lib)
-                      expression-trees)))
-       (if context
-           ;; Evaluate immediately.
-           (string-join (map (lambda (value)
-                               (if (string? value)
-                                   value
-                                   (scm->json-string (canonicalize-json value))))
-                             vals)
-                        "")
-           ;; Compile to a G-expression that interpolates the javascript
-           ;; expression string.
-           #~(string-join (map (lambda (value)
+  (define (trim-left expression-trees)
+    ;; Drop first expression tree if it is a whitespace string.
+    (match expression-trees
+      (() '())
+      ((first-expression-tree other-expression-trees ...)
+       (if (and (string? first-expression-tree)
+                (string-every char-set:whitespace first-expression-tree))
+           other-expression-trees
+           expression-trees))))
+
+  (define (trim-right expression-trees)
+    ;; Drop last expression tree if it is a whitespace string.
+    (match expression-trees
+      (() '())
+      (_
+       (let ((last-expression-tree (last expression-trees)))
+         (if (and (string? last-expression-tree)
+                  (string-every char-set:whitespace last-expression-tree))
+             (drop-right expression-trees 1)
+             expression-trees)))))
+
+  (define trim
+    (compose trim-left trim-right))
+
+  (define evaluate
+    (match-lambda
+      ((expression-tree)
+       ;; There is only one expression. This is not a string interpolation. Do
+       ;; not serialize JSON.
+       (evaluate-expression-tree-1 expression-tree
+                                   context
+                                   expression-lib))
+      ;; This is a string interpolation. Evaluate expressions and serialize
+      ;; JSON.
+      ((expression-trees ...)
+       (let ((vals (map (cut evaluate-expression-tree-1 <> context expression-lib)
+                        expression-trees)))
+         (if context
+             ;; Evaluate immediately.
+             (string-join (map (lambda (value)
                                  (if (string? value)
                                      value
                                      (scm->json-string (canonicalize-json value))))
-                               (list #$@vals))
-                          ""))))))
+                               vals)
+                          "")
+             ;; Compile to a G-expression that interpolates the javascript
+             ;; expression string.
+             #~(string-join (map (lambda (value)
+                                   (if (string? value)
+                                       value
+                                       (scm->json-string (canonicalize-json value))))
+                                 (list #$@vals))
+                            ""))))))
+
+  (match (peg:tree (match-pattern javascript str))
+    (('javascript expression-trees ...)
+     (evaluate (trim expression-trees)))))
diff --git a/tests/javascript.scm b/tests/javascript.scm
index eb70373..133e65e 100644
--- a/tests/javascript.scm
+++ b/tests/javascript.scm
@@ -1,5 +1,5 @@
 ;;; ravanan --- High-reproducibility CWL runner powered by Guix
-;;; Copyright © 2024–2025 Arun Isaac <arunisaac@systemreboot.net>
+;;; Copyright © 2024–2026 Arun Isaac <arunisaac@systemreboot.net>
 ;;;
 ;;; This file is part of ravanan.
 ;;;
@@ -214,4 +214,20 @@
   (evaluate-javascript-expression " $(1 + 1)\n"
                                   '()))
 
+(test-equal "allow whitespace characters in between javascript expressions"
+  "2\t5"
+  (evaluate-javascript-expression "$(1 + 1)\t$(2 + 3)\n"
+                                  '()))
+
+(test-equal "allow string literal with whitespace characters in between javascript expressions"
+  "2foo 5"
+  (evaluate-javascript-expression "$(1 + 1)foo $(2 + 3)\n"
+                                  '()))
+
+(test-equal "complex awk expression that has whitespace characters and javascript expressions"
+  "($1 == \"foo\") && (start <= $2) && ($2 <= end)"
+  (evaluate-javascript-expression "($1 == \"$(inputs.label)\") && (start <= $2) && ($2 <= end)"
+                                  '(("inputs"
+                                     ("label" . "foo")))))
+
 (test-end "javascript")