diff options
| author | Arun Isaac | 2026-01-28 19:09:10 +0000 |
|---|---|---|
| committer | Arun Isaac | 2026-01-28 21:02:19 +0000 |
| commit | d980fcd1a5fdbe0fc5e1833c7366903d4b0c3685 (patch) | |
| tree | 2fdd0012c06af04719956721d682abbdcd1ceb6a | |
| parent | 55d04bbe46581d6e0f3da97f7b9339ccf5738d21 (diff) | |
| download | ravanan-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.scm | 96 | ||||
| -rw-r--r-- | tests/javascript.scm | 18 |
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") |
