diff --git a/backend/src/main/scala/com/griddynamics/genesis/template/dsl/groovy/transformations/MacroASTTransformation.scala b/backend/src/main/scala/com/griddynamics/genesis/template/dsl/groovy/transformations/MacroASTTransformation.scala index 2d7cc8d53..1d06824e2 100644 --- a/backend/src/main/scala/com/griddynamics/genesis/template/dsl/groovy/transformations/MacroASTTransformation.scala +++ b/backend/src/main/scala/com/griddynamics/genesis/template/dsl/groovy/transformations/MacroASTTransformation.scala @@ -68,11 +68,20 @@ class ApplyVariablesVisitor(values: Map[String, Expression], replacements: mutab case variable: VariableExpression => { values.get(variable.getName).getOrElse(variable) } + case constant: ConstantExpression if constant.getText.startsWith("$") => values.get(constant.getText).getOrElse(constant) + case cast: CastExpression => { new CastExpression(cast.getType, transform(cast.getExpression), cast.isIgnoringAutoboxing) } + + case elvis: ElvisOperatorExpression => { + val trueExpr: Expression = transform(elvis.getTrueExpression) + val be = new BooleanExpression(trueExpr) + be.setSourcePosition(trueExpr) + new ElvisOperatorExpression(be, transform(elvis.getFalseExpression)) + } case other => other } } @@ -115,11 +124,11 @@ class MacroExpandVisitor(val macrodefs: Map[String, Macro], replacements: mutabl private def applyArguments(values: Map[String, Expression], code: BlockStatement) : BlockStatement = { val visitor = new ApplyVariablesVisitor(values, replacements) - log.debug(s"Applying variables $values") + log.trace(s"Applying variables $values") val copy = copyBlock(code) log.trace(s"Initial code is: $code") copy.visit(visitor) - log.debug(s"Copy with expanded variables: $copy") + log.trace(s"Copy with expanded variables: $copy") copy } @@ -147,13 +156,18 @@ class MacroExpandVisitor(val macrodefs: Map[String, Macro], replacements: mutabl case method: MethodCallExpression => new MethodCallExpression(copy(method.getObjectExpression), copy(method.getMethod), copy(method.getArguments)) case c: ConstructorCallExpression => new ConstructorCallExpression(c.getType, copy(c.getArguments)) - case elvis: ElvisOperatorExpression => { - val trueExpression = copy(elvis.getTrueExpression) - val falseExpression = copy(elvis.getFalseExpression) - val base = new BooleanExpression(trueExpression) - base.setSourcePosition(trueExpression) - new ElvisOperatorExpression(base, falseExpression) - } + case elvis: ElvisOperatorExpression => copyElvisExpression(elvis) + } + + private def copyElvisExpression(elvis: ElvisOperatorExpression) : ElvisOperatorExpression = { + log.debug(s"Input: ${elvis.getText}") + val trueExpression = copy(elvis.getTrueExpression) + val falseExpression = copy(elvis.getFalseExpression) + val base = new BooleanExpression(trueExpression) + base.setSourcePosition(trueExpression) + val result = new ElvisOperatorExpression(base, falseExpression) + log.debug(s"Output: ${elvis.getText}") + result } private def copyClosureExpression(closure: ClosureExpression): ClosureExpression = { @@ -169,6 +183,10 @@ class MacroExpandVisitor(val macrodefs: Map[String, Macro], replacements: mutabl bs.addStatement(new ExpressionStatement(copy(be))) } + private def addElvisExpression(elvis: ElvisOperatorExpression, bs: BlockStatement) = { + bs.addStatement(new ExpressionStatement(copy(elvis))) + } + private def addMethodCall(objectExpr: Expression, method: String, arguments: Expression, statement: BlockStatement) { val newExpr = new MethodCallExpression(objectExpr, method, copy(arguments)) statement.addStatement(new ExpressionStatement(newExpr)) @@ -193,6 +211,7 @@ class MacroExpandVisitor(val macrodefs: Map[String, Macro], replacements: mutabl case be: BinaryExpression => addBinaryExpression(be, blockStatement) case method: MethodCallExpression => addMethodCall(method.getObjectExpression, method.getMethodAsString, method.getArguments, blockStatement) case map: MapExpression => addMapExpression(map, blockStatement) + case elvis: ElvisOperatorExpression => addElvisExpression(elvis, blockStatement) } } } diff --git a/backend/src/test/resources/groovy/Macros.genesis b/backend/src/test/resources/groovy/Macros.genesis index 97014aca2..e5d0d165b 100644 --- a/backend/src/test/resources/groovy/Macros.genesis +++ b/backend/src/test/resources/groovy/Macros.genesis @@ -23,6 +23,12 @@ template { } } + defmacro "elvis" : { $var -> + teststep { + text = $system.databag['macros'].keya ?: $var + } + } + defmacro "map": { $key, $mapValue -> withMap { text = $vars.myvar @@ -76,6 +82,10 @@ template { macro:create_steps() macro:simple() macro:create_steps($message: xxx) + teststep { + text = $databags['macros'].keya ?: "local" + } + macro:elvis("redefine") } } diff --git a/backend/src/test/scala/com/griddynamics/genesis/service/impl/MacrosTest.scala b/backend/src/test/scala/com/griddynamics/genesis/service/impl/MacrosTest.scala index 96123e2b4..77af2f038 100644 --- a/backend/src/test/scala/com/griddynamics/genesis/service/impl/MacrosTest.scala +++ b/backend/src/test/scala/com/griddynamics/genesis/service/impl/MacrosTest.scala @@ -15,7 +15,7 @@ import com.griddynamics.genesis.template.VersionedTemplate import com.griddynamics.genesis.api import com.griddynamics.genesis.plugin.{StepBuilderFactory, StepBuilder} import com.griddynamics.genesis.service.{VariableDescription, TemplateDefinition} -import com.griddynamics.genesis.api.Failure +import com.griddynamics.genesis.api.{DataBag, Failure} import com.griddynamics.genesis.workflow.Step import scala.beans.BeanProperty import scala.collection.mutable @@ -32,13 +32,14 @@ class MacrosTest extends AssertionsForJUnit with MockitoSugar with DSLTestUniver Mockito.when(templateRepository.listSources()).thenReturn(Map(VersionedTemplate("1") -> body)) when(configService.get(Matchers.any(), Matchers.any())).thenReturn(Some(new api.Configuration(Some(0), "", 0, None, Map()))) + when(databagRepository.findByName("macros")).thenReturn(Some(new DataBag(None, "macros", Seq()))) @Test def testStepsInserted() { val template: Option[TemplateDefinition] = templateService.findTemplate(0, "Macros", "0.1", 0) val workflow = template.flatMap(_.getWorkflow("macros")).get val steps = workflow.embody(Map()) - expectResult(6)(steps.regular.size) + expectResult(8)(steps.regular.size) val initialPhase: Option[StepBuilder] = steps.regular.find(_.phase == "auto_0") val secondPhase: Option[StepBuilder] = steps.regular.find(_.phase == "auto_1") assert(initialPhase.isDefined) @@ -46,7 +47,7 @@ class MacrosTest extends AssertionsForJUnit with MockitoSugar with DSLTestUniver assert(secondPhase.isDefined) assert(secondPhase.get.getPrecedingPhases.contains("auto_0")) steps.regular.zip(Seq("Static", "Passed from macro call", - "Set with map", "default", "Set from constant", "Call from closure")).map({ + "Set with map", "default", "Set from constant", "Call from closure", "local", "redefine")).map({ case (step, message) => step.newStep.actualStep match { case nothing: DoNothingStep => expectResult(message)(nothing.name) }