Skip to content

Commit

Permalink
feat: output statement (#1262)
Browse files Browse the repository at this point in the history
Closes #1259

### Summary of Changes

Add a new kind of statement, the output statement, to inspect results of
expressions without creating useless placeholders.
  • Loading branch information
lars-reimann authored Nov 10, 2024
1 parent a651ade commit 011ba31
Show file tree
Hide file tree
Showing 62 changed files with 1,348 additions and 532 deletions.
18 changes: 15 additions & 3 deletions docs/api/safeds/data/tabular/containers/Column.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pipeline example {
*/
attr type: DataType

/*
/**
* Return the distinct values in the column.
*
* @param ignoreMissingValues Whether to ignore missing values.
Expand Down Expand Up @@ -915,17 +915,29 @@ pipeline example {

## <code class="doc-symbol doc-symbol-function"></code> `getDistinctValues` {#safeds.data.tabular.containers.Column.getDistinctValues data-toc-label='[function] getDistinctValues'}

Return the distinct values in the column.

**Parameters:**

| Name | Type | Description | Default |
|------|------|-------------|---------|
| `ignoreMissingValues` | [`Boolean`][safeds.lang.Boolean] | - | `#!sds true` |
| `ignoreMissingValues` | [`Boolean`][safeds.lang.Boolean] | Whether to ignore missing values. | `#!sds true` |

**Results:**

| Name | Type | Description |
|------|------|-------------|
| `distinctValues` | [`List<T?>`][safeds.lang.List] | - |
| `distinctValues` | [`List<T?>`][safeds.lang.List] | The distinct values in the column. |

**Examples:**

```sds hl_lines="3"
pipeline example {
val column = Column("test", [1, 2, 3, 2]);
val result = column.getDistinctValues();
// [1, 2, 3]
}
```

??? quote "Stub code in `Column.sdsstub`"

Expand Down
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ nav:
- pipeline-language/statements/README.md
- Expression Statements: pipeline-language/statements/expression-statements.md
- Assignments: pipeline-language/statements/assignments.md
- Output Statements: pipeline-language/statements/output-statements.md
- Expressions:
- pipeline-language/expressions/README.md
- Literals: pipeline-language/expressions/literals.md
Expand Down
12 changes: 7 additions & 5 deletions docs/pipeline-language/statements/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# Statements

Statements are used to run some action. Safe-DS has only two type of statements:
Statements are used to run some action. Safe-DS only has three type of statements:

- [Expression statements][expression-statements] evaluate an [expression][expressions] and discard any results. They are
only useful if the expression has side effects, such as writing to a file.
- [Assignments][assignments] also evaluate an [expression][expressions], but then store results in
[placeholders][placeholders]. This allows reusing the results multiple times without having to recompute them.
- [Output statements][output-statements] evaluate an [expression][expressions] as well, and provide options to inspect
its results. Unlike when using assignments, the result cannot be reused.


[assignments]: ./assignments.md
[expression-statements]: ./expression-statements.md
[assignments]: assignments.md
[expression-statements]: expression-statements.md
[output-statements]: output-statements.md
[expressions]: ../expressions/README.md
[placeholders]: ./assignments.md#declaring-placeholders
[placeholders]: assignments.md#declaring-placeholders
6 changes: 4 additions & 2 deletions docs/pipeline-language/statements/assignments.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ This assignment to a placeholder has the following syntactic elements:
- The name of the placeholder, here `titanic`. It can be any combination of lower- and uppercase letters, underscores,
and numbers, as long as it does not start with a number.
- An `#!sds =` sign.
- The expression to evaluate (right-hand side).
- The [expression][expressions] to evaluate (right-hand side).
- A semicolon at the end.

??? info "Name convention"

Use `#!sds lowerCamelCase` for the name of the placeholder. You may prefix the name of an unused placeholder with an
underscore (`_`) to indicate that it is intentionally unused, e.g. to
[inspect its value](#inspecting-placeholder-values-in-vs-code). This disables the "unused" warning.
[inspect its value](#inspecting-placeholder-values-in-vs-code). This disables the "unused" warning. For value
inspection, also consider using an [output statement][output-statements] instead.

### References to Placeholder

Expand Down Expand Up @@ -104,6 +105,7 @@ such cases.
[expressions]: ../expressions/README.md
[expression-statements]: expression-statements.md
[installation]: ../../getting-started/installation.md
[output-statements]: output-statements.md
[references]: ../expressions/references.md#references
[runner]: https://github.com/Safe-DS/Runner
[results]: ../segments.md#results
26 changes: 26 additions & 0 deletions docs/pipeline-language/statements/output-statements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Output Statements

Output statements are used to evaluate an expression and inspect its results. Unlike when using assignments, the results
cannot be reused. However, it is also not necessary to think of unique names for placeholders, which saves time and
keeps the namespace clean.

The next snippet shows how the singular result of an expression (the loaded
[`Table`][safeds.data.tabular.containers.Table]) can be inspected:

```sds
out Table.fromCsvFile("titanic.csv");
```

This output statement has the following syntactic elements:

- The keyword `#!sds out`, which indicates that we want to inspect the results of an expression.
- The expression to evaluate.
- A semicolon at the end.

Inspecting values requires a working installation of the [Safe-DS Runner][runner]. Follow the instructions in the
[installation guide][installation] to install it. Afterward, you can inspect values of various types via
_code lenses_ in the editor, as explained for [assignments][value-inspection].

[installation]: ../../getting-started/installation.md
[runner]: https://github.com/Safe-DS/Runner
[value-inspection]: assignments.md#inspecting-placeholder-values-in-vs-code
2 changes: 1 addition & 1 deletion packages/safe-ds-cli/src/cli/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const generate = async (fsPaths: string[], options: GenerateOptions): Pro
const generatedFiles = services.generation.PythonGenerator.generate(document, {
destination: URI.file(path.resolve(options.out)),
createSourceMaps: options.sourcemaps,
targetPlaceholders: undefined,
targetStatements: undefined,
disableRunnerIntegration: false,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const COMMAND_EXPLORE_TABLE = 'safe-ds.exploreTable';
export const COMMAND_PRINT_VALUE = 'safe-ds.printValue';
export const COMMAND_RUN_PIPELINE = 'safe-ds.runPipeline';
export const COMMAND_SHOW_IMAGE = 'safe-ds.showImage';
34 changes: 34 additions & 0 deletions packages/safe-ds-lang/src/language/communication/rpc.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { MessageDirection, NotificationType0, RequestType0 } from 'vscode-languageserver';
import { NotificationType } from 'vscode-languageserver-protocol';
import { UUID } from 'node:crypto';

export namespace InstallRunnerNotification {
export const method = 'runner/install' as const;
Expand Down Expand Up @@ -32,6 +33,39 @@ export namespace UpdateRunnerNotification {
export const type = new NotificationType0(method);
}

export namespace ExploreTableNotification {
export const method = 'runner/exploreTable' as const;
export const messageDirection = MessageDirection.serverToClient;
export const type = new NotificationType<ExploreTableNotification>(method);
}

export interface ExploreTableNotification {
/**
* The ID of the pipeline execution.
*/
pipelineExecutionId: UUID;

/**
* The URI of the pipeline document.
*/
uri: string;

/**
* The name of the pipeline.
*/
pipelineName: string;

/**
* The end offset of the pipeline node. This is used to add more code to the pipeline by the EDA tool.
*/
pipelineNodeEndOffset: number;

/**
* The name of the placeholder containing the table.
*/
placeholderName: string;
}

export namespace ShowImageNotification {
export const method = 'runner/showImage' as const;
export const messageDirection = MessageDirection.serverToClient;
Expand Down
27 changes: 16 additions & 11 deletions packages/safe-ds-lang/src/language/flow/safe-ds-slicer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@ export class SafeDsSlicer {
/**
* Computes the subset of the given statements that are needed to calculate the target placeholders.
*/
computeBackwardSlice(statements: SdsStatement[], targets: SdsPlaceholder[]): SdsStatement[] {
const aggregator = new BackwardSliceAggregator(this.purityComputer, targets);
computeBackwardSliceToTargets(statements: SdsStatement[], targets: SdsStatement[]): SdsStatement[] {
const aggregator = new BackwardSliceAggregator(this.purityComputer);

for (const statement of statements.reverse()) {
// Keep if it declares a target
if (
// Keep if it is a target
if (targets.includes(statement)) {
aggregator.addStatement(statement);
}

// Keep if it declares a referenced placeholder
else if (
isSdsAssignment(statement) &&
getAssignees(statement).some((it) => isSdsPlaceholder(it) && aggregator.targets.has(it))
getAssignees(statement).some((it) => isSdsPlaceholder(it) && aggregator.referencedPlaceholders.has(it))
) {
aggregator.addStatement(statement);
}
Expand All @@ -49,32 +54,32 @@ class BackwardSliceAggregator {
private readonly purityComputer: SafeDsPurityComputer;

/**
* The statements that are needed to calculate the target placeholders.
* The statements that are needed to calculate the target statements.
*/
readonly statements: SdsStatement[] = [];

/**
* The target placeholders that should be calculated.
* The placeholders that are needed to calculate the target statements.
*/
readonly targets: Set<SdsPlaceholder>;
readonly referencedPlaceholders: Set<SdsPlaceholder>;

/**
* The impurity reasons of the collected statements.
*/
readonly impurityReasons: ImpurityReason[] = [];

constructor(purityComputer: SafeDsPurityComputer, initialTargets: SdsPlaceholder[]) {
constructor(purityComputer: SafeDsPurityComputer) {
this.purityComputer = purityComputer;

this.targets = new Set(initialTargets);
this.referencedPlaceholders = new Set();
}

addStatement(statement: SdsStatement): void {
this.statements.unshift(statement);

// Remember all referenced placeholders
this.getReferencedPlaceholders(statement).forEach((it) => {
this.targets.add(it);
this.referencedPlaceholders.add(it);
});

// Remember all impurity reasons
Expand Down
Loading

0 comments on commit 011ba31

Please sign in to comment.