Skip to content

Commit

Permalink
Generate .actnow in guided meditations
Browse files Browse the repository at this point in the history
This creates a new `Run` terminal in `RepeatFor` that allows generating
`.actnow` files in a guided meditations.
  • Loading branch information
apmasell committed Nov 17, 2023
1 parent e1bff53 commit d95070b
Show file tree
Hide file tree
Showing 18 changed files with 350 additions and 17 deletions.
1 change: 1 addition & 0 deletions changes/add_medidate_now.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Add `Run` to `RepeatFor` in guided meditations to generate `.actnow` files.
5 changes: 5 additions & 0 deletions guided-meditations.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ as if they had been written out repeatedly.
The items will be placed in rows in a table. Each column starts with a string
_header_ and then the text display information to fill that cell in _value_.

- ... `Run` _action_ [ `Tag` _tag_ ] `With` _parameter_`,` ... `As` _filename_

The items will be converted to an `.actnow` file that can be downloaded. This
works with the same syntax as the normal `Run` in an olive. The file will be
downloaded as _filename_`.actnow`.

<a name="simulation">
### Simulations
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package ca.on.oicr.gsi.shesmu.compiler;

import ca.on.oicr.gsi.shesmu.compiler.definitions.ActionDefinition;
import ca.on.oicr.gsi.shesmu.compiler.definitions.FunctionDefinition;
import ca.on.oicr.gsi.shesmu.compiler.definitions.InputFormatDefinition;
import ca.on.oicr.gsi.shesmu.plugin.types.Imyhat;
import java.util.Optional;
import java.util.function.Consumer;

public interface ExpressionCompilerServices {
ActionDefinition action(String name);

default Optional<TargetWithContext> captureOptional(
ExpressionNode expression, int line, int column, Consumer<String> errorHandler) {
errorHandler.accept(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static ca.on.oicr.gsi.shesmu.compiler.TypeUtils.TO_ASM;

import ca.on.oicr.gsi.shesmu.compiler.Target.Flavour;
import ca.on.oicr.gsi.shesmu.compiler.definitions.ActionDefinition;
import ca.on.oicr.gsi.shesmu.compiler.definitions.FunctionDefinition;
import ca.on.oicr.gsi.shesmu.compiler.definitions.InputFormatDefinition;
import ca.on.oicr.gsi.shesmu.plugin.types.Imyhat;
Expand Down Expand Up @@ -49,6 +50,11 @@ public OptionalCaptureCompilerServices(
this.layer = layer;
}

@Override
public ActionDefinition action(String name) {
return expressionCompilerServices.action(name);
}

@Override
public Optional<TargetWithContext> captureOptional(
ExpressionNode expression, int line, int column, Consumer<String> errorHandler) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,43 @@ private interface SimulationConstructor {
new InformationNodeRepeat(name, source, transforms, collectors.get()));
return result;
});

REPEAT_COLLECTOR.addKeyword(
"Run",
(input, output) -> {
final var actionName = new AtomicReference<String>();
final var variableTags = new AtomicReference<List<VariableTagNode>>();
final var arguments = new AtomicReference<List<OliveArgumentNode>>();
final var fileName = new AtomicReference<ExpressionNode>();
final var result =
input
.whitespace()
.qualifiedIdentifier(actionName::set)
.whitespace()
.list(variableTags::set, VariableTagNode::parse)
.whitespace()
.keyword("With")
.whitespace()
.list(arguments::set, OliveArgumentNode::parse, ',')
.whitespace()
.keyword("As")
.whitespace()
.then(ExpressionNode::parse, fileName::set);
if (result.isGood()) {
output.accept(
(name, source, transforms) ->
new InformationNodeActNow(
input.line(),
input.column(),
name,
source,
transforms,
actionName.get(),
variableTags.get(),
arguments.get(),
fileName.get()));
}
return result;
});
DISPATCH.addKeyword(
"Alerts",
(p, o) -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package ca.on.oicr.gsi.shesmu.compiler;

import ca.on.oicr.gsi.shesmu.compiler.definitions.ActionDefinition;
import ca.on.oicr.gsi.shesmu.compiler.definitions.ActionParameterDefinition;
import ca.on.oicr.gsi.shesmu.compiler.definitions.DefinitionRepository;
import ca.on.oicr.gsi.shesmu.plugin.types.Imyhat;
import ca.on.oicr.gsi.shesmu.runtime.RuntimeSupport;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

public class InformationNodeActNow extends InformationNodeBaseRepeat {
private final int line;
private final int column;
private final List<OliveArgumentNode> arguments;
private final List<VariableTagNode> variableTags;
private ActionDefinition definition;
private final ExpressionNode fileName;
private final String actionName;

public InformationNodeActNow(
int line,
int column,
DestructuredArgumentNode name,
SourceNode source,
List<ListNode> transforms,
String actionName,
List<VariableTagNode> variableTags,
List<OliveArgumentNode> arguments,
ExpressionNode fileName) {
super(name, source, transforms);
this.line = line;
this.column = column;
this.arguments = arguments;
this.variableTags = variableTags;
this.fileName = fileName;
this.actionName = actionName;
}

@Override
protected String renderBlock(EcmaScriptRenderer renderer, String data) {
return String.format(
"{type: \"download\", isJson: true, mimetype: \"application/json\", contents: %s, file: (%s) + \".actnow\"}",
data, fileName.renderEcma(renderer));
}

@Override
public String renderRow(EcmaScriptRenderer renderer) {
try {
return String.format(
"{name: %s, parameters: {%s}, tags: [%s]}",
RuntimeSupport.MAPPER.writeValueAsString(definition.name()),
arguments.stream()
.map(argument -> argument.renderEcma(renderer))
.collect(Collectors.joining(", ")),
variableTags.stream()
.map(tag -> tag.renderEcma(renderer))
.collect(Collectors.joining(", ")));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}

@Override
protected boolean resolveTerminal(
NameDefinitions parentName, NameDefinitions collectorName, Consumer<String> errorHandler) {
var ok =
arguments.stream()
.filter(collector -> collector.resolve(collectorName, errorHandler))
.count()
== arguments.size();
if (ok) {

final var argumentNames =
arguments.stream()
.flatMap(OliveArgumentNode::targets)
.collect(Collectors.groupingBy(Target::name, Collectors.counting()));
for (final var entry : argumentNames.entrySet()) {
if (entry.getValue() > 1) {
errorHandler.accept(
String.format(
"%d:%d: Duplicate argument “%s” to action.", line, column, entry.getKey()));
ok = false;
}
}

final var definedArgumentNames =
definition.parameters().map(ActionParameterDefinition::name).collect(Collectors.toSet());
final var requiredArgumentNames =
definition
.parameters()
.filter(ActionParameterDefinition::required)
.map(ActionParameterDefinition::name)
.collect(Collectors.toSet());
if (!definedArgumentNames.containsAll(argumentNames.keySet())) {
ok = false;
final Set<String> badTerms = new HashSet<>(argumentNames.keySet());
badTerms.removeAll(definedArgumentNames);
errorHandler.accept(
String.format(
"%d:%d: Extra arguments for action %s: %s",
line, column, actionName, String.join(", ", badTerms)));
}
switch (arguments.stream()
.map(argument -> argument.checkWildcard(errorHandler))
.reduce(WildcardCheck.NONE, WildcardCheck::combine)) {
case NONE:
if (!argumentNames.keySet().containsAll(requiredArgumentNames)) {
ok = false;
final Set<String> badTerms = new HashSet<>(requiredArgumentNames);
badTerms.removeAll(argumentNames.keySet());
errorHandler.accept(
String.format(
"%d:%d: Missing arguments for action %s: %s",
line, column, actionName, String.join(", ", badTerms)));
}
break;
case HAS_WILDCARD:
final var provider =
arguments.stream()
.map(x -> (UndefinedVariableProvider) x)
.reduce(UndefinedVariableProvider.NONE, UndefinedVariableProvider::combine);
for (final var requiredName : requiredArgumentNames) {
if (!definedArgumentNames.contains(requiredName)) {
provider.handleUndefinedVariable(requiredName);
}
}
break;
case BAD:
ok = false;
break;
}
}
return ok
& variableTags.stream().filter(tag -> tag.resolve(collectorName, errorHandler)).count()
== variableTags.size()
& fileName.resolve(parentName, errorHandler);
}

@Override
protected boolean resolveTerminalDefinitions(
ExpressionCompilerServices expressionCompilerServices,
DefinitionRepository nativeDefinitions,
Consumer<String> errorHandler) {
definition = expressionCompilerServices.action(actionName);
if (definition == null) {
errorHandler.accept(
String.format("%d:%d: Unknown action for “%s”.", line, column, actionName));
}
return definition != null
& arguments.stream()
.filter(arg -> arg.resolveFunctions(expressionCompilerServices, errorHandler))
.count()
== arguments.size()
& variableTags.stream()
.filter(tag -> tag.resolveDefinitions(expressionCompilerServices, errorHandler))
.count()
== variableTags.size()
& fileName.resolveDefinitions(expressionCompilerServices, errorHandler);
}

@Override
protected boolean typeCheckTerminal(Consumer<String> errorHandler) {
var ok = fileName.typeCheck(errorHandler);
if (ok && !fileName.type().isSame(Imyhat.STRING)) {
fileName.typeError(Imyhat.STRING, fileName.type(), errorHandler);
ok = false;
}
for (final var tag : variableTags) {
if (!tag.typeCheck(errorHandler)) {
ok = false;
}
}
if (arguments.stream()
.filter(
argument -> argument.typeCheck(errorHandler) && argument.checkName(errorHandler))
.count()
== arguments.size()) {
final var parameterInfo =
definition
.parameters()
.collect(Collectors.toMap(ActionParameterDefinition::name, Function.identity()));
return arguments.stream()
.filter(argument -> argument.checkArguments(parameterInfo::get, errorHandler))
.count()
== arguments.size()
& ok;
} else {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public final boolean resolve(NameDefinitions defs, Consumer<String> errorHandler
.map(
name -> {
final var collectorName = defs.replaceStream(name.targets(), true);
return resolveTerminal(collectorName, errorHandler);
return resolveTerminal(defs, collectorName, errorHandler);
})
.orElse(false);
return ok;
Expand All @@ -79,7 +79,7 @@ & resolveTerminalDefinitions(expressionCompilerServices, nativeDefinitions, erro
}

protected abstract boolean resolveTerminal(
NameDefinitions collectorName, Consumer<String> errorHandler);
NameDefinitions parentName, NameDefinitions collectorName, Consumer<String> errorHandler);

protected abstract boolean resolveTerminalDefinitions(
ExpressionCompilerServices expressionCompilerServices,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public String renderRow(EcmaScriptRenderer renderer) {
}

@Override
protected boolean resolveTerminal(NameDefinitions collectorName, Consumer<String> errorHandler) {
protected boolean resolveTerminal(
NameDefinitions parentName, NameDefinitions collectorName, Consumer<String> errorHandler) {
return collectors.stream()
.filter(collector -> collector.resolve(collectorName, errorHandler))
.count()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ public String renderRow(EcmaScriptRenderer renderer) {
}

@Override
protected boolean resolveTerminal(NameDefinitions collectorName, Consumer<String> errorHandler) {
protected boolean resolveTerminal(
NameDefinitions parentName, NameDefinitions collectorName, Consumer<String> errorHandler) {
return columns.stream()
.filter(collector -> collector.resolve(collectorName, errorHandler))
.count()
Expand Down
Loading

0 comments on commit d95070b

Please sign in to comment.