Skip to content

Commit 2beb60b

Browse files
authored
AN-733 WDL 1.1: Call input passthrough syntax (#7815)
1 parent b0a32c9 commit 2beb60b

File tree

18 files changed

+2933
-2416
lines changed

18 files changed

+2933
-2416
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Cromwell Change Log
22

3+
## 92 Release Notes
4+
5+
### Progress toward WDL 1.1 Support
6+
* WDL 1.1 support is in progress. Users that would like to try out the current partial support can do so by using WDL version `development-1.1`. In Cromwell 92, `development-1.1` has been enhanced to include:
7+
* Support for passthrough syntax for call inputs, e.g. `{ input: foo }` rather than `{ input: foo = foo }`.
8+
39
## 91 Release Notes
410

511
#### Removal of Google LifeSciences backend code

docs/developers/UpdateWDLParser.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
This page contains instructions for using [Hermes](https://github.com/scottfrazer/hermes) to regenerate the WDL parser after making changes to the WDL grammar file, which is located at `wdl/transforms/$version/src/main/resources/grammar.hgr`. Note that there is a separate grammar file and parser for each WDL version.
2+
3+
## Install Hermes
4+
5+
Requires Python 3 (tested with Python 3.5). [Pyenv](https://github.com/pyenv/pyenv) is the recommended way to install this older Python version. Newer Python versions may not work correctly.
6+
7+
Hermes can be installed via pip (https://pypi.python.org/pypi/hermes-parser/2.0rc6)
8+
9+
However, It is recommended that Hermes is installed from source into a virtual environment because PyPI isn’t updated very often.
10+
11+
First, clone Hermes and create then activate a new Python virtual environment (in directory ./hermes-venv)
12+
```
13+
$ git clone [email protected]:scottfrazer/hermes.git
14+
$ cd hermes
15+
$ pyenv virtualenv 3.5.10 hermes-venv
16+
$ pyenv activate hermes-venv
17+
```
18+
19+
In the Hermes repo, make sure you have the latest ‘develop’ branch checked out. Then, install Hermes into the virtual environment (need to separately run pip installs because the Python 3.5 default index URL doesn't work anymore):
20+
```
21+
$ pip install --index-url https://pypi.org/simple/ moody-templates xtermcolor nose pygments hermes_pygments
22+
$ python setup.py install
23+
$ hermes --help
24+
```
25+
26+
With the virtual environment activated, navigate to `cromwell/wdl/transforms/$version` and run the below command, replacing `$version` with the correct package name (e.g. `biscayne` or `cascades`).:
27+
```
28+
hermes generate src/main/resources/grammar.hgr \
29+
--language=java \
30+
--directory=src/main/java \
31+
--name=wdl \
32+
--java-package=wdl.$version.parser \
33+
--java-use-apache-commons --java-imports=org.apache.commons.lang3.StringEscapeUtils \
34+
--header
35+
```

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ nav:
7575
- How to Build: developers/Building.md
7676
- Architecture: developers/Arch.md
7777
- Backend Development: developers/Backend.md
78+
- Update WDL Parsers: developers/UpdateWDLParser.md
7879
- Instrumentation: developers/Instrumentation.md
7980
- Integration tests: developers/Centaur.md
8081
- Security: developers/Security.md

wdl/transforms/biscayne/src/main/java/wdl/biscayne/parser/WdlParser.java

Lines changed: 1285 additions & 1187 deletions
Large diffs are not rendered by default.

wdl/transforms/biscayne/src/main/resources/CHANGELOG.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
# Changelog
12
2023-03-16
23
Synced grammar from OpenWDL `development` version, which is actually development of 2.0. There is no 1.1 Hermes grammar, develop it here.
34
Changed version declaration to `development1_1`.
45
This disallows `version 1.1` workflows to run with incomplete support. Once development is finished, change to `1.1`.
56

67
2024-02-28
8+
Added support for struct literals
9+
10+
2025-09-30
11+
Added support for passthrough task inputs (supply `foo` rather than `foo=foo` when a local variable and task input share the same name).
12+
13+
# HowTo
714
When changing the grammar file, generate a new parser by:
815
- changing current working directory to cromwell/wdl/transforms/biscayne
916
- running: hermes generate src/main/resources/grammar.hgr \

wdl/transforms/biscayne/src/main/resources/grammar.hgr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,10 @@ grammar {
429429
$wf_body_element = $call | $declaration | $if_stmt | $scatter | $inputs | $outputs | $wf_parameter_meta | $wf_meta
430430
$call = :call :fqn optional($alias) list($call_after) optional($call_brace_block) -> Call(task=$1, alias=$2, after=$3, body=$4)
431431
$call_brace_block = :lbrace optional($call_body) :rbrace -> $1
432-
$call_body = :input :colon list($input_kv, :comma) -> CallBody(inputs=$2)
432+
$call_body = :input :colon list($call_input, :comma) -> CallBody(inputs=$2)
433+
$call_input = :identifier optional($input_assignment) -> CallInputKV(key=$0, value=$1)
434+
$input_assignment = :equal $e -> $1
435+
433436
$alias = :as :identifier -> $1
434437
$call_after = :after :identifier -> $1
435438
$wf_parameter_meta = :parameter_meta $meta_map -> ParameterMeta(map=$1)
@@ -440,6 +443,5 @@ grammar {
440443
-> Scatter(item=$2, collection=$4, body=$7)
441444

442445
$object_kv = :identifier :colon $e -> ObjectKV(key=$0, value=$2)
443-
$input_kv = :identifier :equal $e -> ObjectKV(key=$0, value=$2)
444446
}
445447
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package wdl.transforms.biscayne.ast2wdlom
2+
3+
import common.transforms.CheckedAtoB
4+
import wdl.model.draft3.elements.ExpressionElement
5+
import wdl.model.draft3.elements.ExpressionElement.KvPair
6+
import wdl.transforms.base.ast2wdlom.{AstNodeToKvPair, GenericAst, GenericAstNode}
7+
8+
object BiscayneAstNodeToKvPair {
9+
def astNodeToKvPair(implicit
10+
astNodeToExpressionElement: CheckedAtoB[GenericAstNode, ExpressionElement]
11+
): CheckedAtoB[GenericAstNode, KvPair] = CheckedAtoB.fromErrorOr {
12+
case i: GenericAst if i.getName == "CallInputKV" =>
13+
// For CallInputKV, we accept either a key/value pair (`foo=foo`) or a plain identifier (`foo`).
14+
// In the latter case, "foo" is both the key and the name of an in-scope variable whose value should
15+
// be passed through. Due to limitations in the parser, both map to the same AST structure,
16+
// with the "value" attribute missing when a passthrough is intended.
17+
val valueKeyName = if (i.getAttributes.contains("value")) "value" else "key"
18+
AstNodeToKvPair.validate(i.getAttributeAs[String]("key"), i.getAttributeAs[ExpressionElement](valueKeyName))
19+
case a: GenericAst if a.getName == "ObjectKV" || a.getName == "MapLiteralKv" || a.getName == "RuntimeAttribute" =>
20+
AstNodeToKvPair.validate(a.getAttributeAs[String]("key"), a.getAttributeAs[ExpressionElement]("value"))
21+
}
22+
}

wdl/transforms/biscayne/src/main/scala/wdl/transforms/biscayne/ast2wdlom/ast2wdlom.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ package object ast2wdlom {
3333
AstToNewExpressionElements.newBiscayneEngineFunctionMakers
3434
)
3535
implicit val astNodeToKvPair: CheckedAtoB[GenericAstNode, KvPair] =
36-
AstNodeToKvPair.astNodeToKvPair(astNodeToExpressionElement)
36+
BiscayneAstNodeToKvPair.astNodeToKvPair(astNodeToExpressionElement)
3737

3838
implicit val astNodeToTypeElement: CheckedAtoB[GenericAstNode, TypeElement] =
3939
AstNodeToTypeElement.astNodeToTypeElement(Map.empty)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
version development-1.1
2+
3+
task sayHi {
4+
input {
5+
String name
6+
String greeting
7+
String inquiry
8+
}
9+
command <<<
10+
cat "~{greeting} ~{name} ~{inquiry}" > /tmp/helloFile
11+
>>>
12+
}
13+
14+
workflow callInputPassthrough {
15+
String name = "friend"
16+
String greeting = "hello"
17+
String inquiry = "how are you?"
18+
19+
# These two inputs should be handled the same way
20+
call sayHi {
21+
input:
22+
name,
23+
greeting = greeting,
24+
inquiry
25+
}
26+
}

wdl/transforms/biscayne/src/test/scala/wdl/transforms/biscayne/ast2wdlom/WdlFileToWdlomSpec.scala

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import org.scalatest.flatspec.AnyFlatSpec
66
import org.scalatest.matchers.should.Matchers
77
import wdl.model.draft3.elements.CommandPartElement.{PlaceholderCommandPartElement, StringCommandPartElement}
88
import wdl.model.draft3.elements.ExpressionElement._
9-
import wdl.model.draft3.elements.{CallElement, FileElement, _}
9+
import wdl.model.draft3.elements._
1010
import wdl.transforms.biscayne.ast2wdlom.WdlFileToWdlomSpec._
1111
import wom.SourceFileLocation
1212
import wom.types._
@@ -467,6 +467,88 @@ object WdlFileToWdlomSpec {
467467
Some(SourceFileLocation(13))
468468
)
469469
)
470-
)
470+
),
471+
"call_input_passthrough" ->
472+
FileElement(
473+
imports = Vector.empty,
474+
structs = Vector.empty,
475+
workflows = Vector(
476+
WorkflowDefinitionElement(
477+
name = "callInputPassthrough",
478+
inputsSection = None,
479+
graphElements = Set(
480+
IntermediateValueDeclarationElement(
481+
PrimitiveTypeElement(WomStringType),
482+
"name",
483+
StringLiteral("friend")
484+
),
485+
IntermediateValueDeclarationElement(
486+
PrimitiveTypeElement(WomStringType),
487+
"greeting",
488+
StringLiteral("hello")
489+
),
490+
IntermediateValueDeclarationElement(
491+
PrimitiveTypeElement(WomStringType),
492+
"inquiry",
493+
StringLiteral("how are you?")
494+
),
495+
CallElement(
496+
"sayHi",
497+
None,
498+
Vector.empty,
499+
Some(
500+
CallBodyElement(
501+
Vector(
502+
KvPair("name", IdentifierLookup("name")),
503+
KvPair("greeting", IdentifierLookup("greeting")),
504+
KvPair("inquiry", IdentifierLookup("inquiry"))
505+
)
506+
)
507+
),
508+
Some(SourceFileLocation(20))
509+
)
510+
),
511+
outputsSection = None,
512+
metaSection = None,
513+
parameterMetaSection = None,
514+
sourceLocation = Some(SourceFileLocation(14))
515+
)
516+
),
517+
tasks = Vector(
518+
TaskDefinitionElement(
519+
name = "sayHi",
520+
inputsSection = Some(
521+
InputsSectionElement(
522+
Vector(
523+
InputDeclarationElement(PrimitiveTypeElement(WomStringType), "name", None),
524+
InputDeclarationElement(PrimitiveTypeElement(WomStringType), "greeting", None),
525+
InputDeclarationElement(PrimitiveTypeElement(WomStringType), "inquiry", None)
526+
)
527+
)
528+
),
529+
declarations = Vector.empty,
530+
outputsSection = None,
531+
commandSection = CommandSectionElement(
532+
List(
533+
CommandSectionLine(
534+
Vector(
535+
StringCommandPartElement("cat \""),
536+
PlaceholderCommandPartElement(IdentifierLookup("greeting"), PlaceholderAttributeSet.empty),
537+
StringCommandPartElement(" "),
538+
PlaceholderCommandPartElement(IdentifierLookup("name"), PlaceholderAttributeSet.empty),
539+
StringCommandPartElement(" "),
540+
PlaceholderCommandPartElement(IdentifierLookup("inquiry"), PlaceholderAttributeSet.empty),
541+
StringCommandPartElement("\" > /tmp/helloFile")
542+
)
543+
)
544+
)
545+
),
546+
runtimeSection = None,
547+
metaSection = None,
548+
parameterMetaSection = None,
549+
sourceLocation = Some(SourceFileLocation(3))
550+
)
551+
)
552+
)
471553
)
472554
}

0 commit comments

Comments
 (0)