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

Flowr Search #1175

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
114 changes: 114 additions & 0 deletions src/search/flowr-search-builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
import type {
FlowrSearchElement,
FlowrSearchElements,
FlowrSearchGeneratorNode, FlowrSearchGetFilters,
FlowrSearchInput, FlowrSearchTransformerNode
} from './flowr-search';
import type { Pipeline } from '../core/steps/pipeline/pipeline';
import type { FlowrFilterExpression } from './flowr-search-filters';
import type { NoInfo } from '../r-bridge/lang-4.x/ast/model/model';


export type FlowrGenerator<P extends Pipeline> = Record<string, (input: FlowrSearchInput<P>) => FlowrSearchElements>

export const FlowrSearchGenerator = {
all(): FlowrSearchBuilder<NoInfo> {
return new FlowrSearchBuilder({ type: 'generator', name: 'all', args: undefined });
},
/**
* TODO TODO TODO
*/
get(filter: FlowrSearchGetFilters): FlowrSearchBuilder<NoInfo> {
return new FlowrSearchBuilder({ type: 'generator', name: 'get', args: filter });
},
/**
* Short form of {@link get} with only the
* {@link FlowrSearchGetFilters#line|line} and {@link FlowrSearchGetFilters#column|column} filters:
* `get({line, column})`.
*/
loc(line?: number, column?: number) {
return FlowrSearchGenerator.get({ line, column });
},
/**
* Short form of {@link get} with only the {@link FlowrSearchGetFilters#name|name} filter:
* `get({name})`.
*/
var(name: string) {
return FlowrSearchGenerator.get({ name });
},
/**
* Short form of {@link get} with only the {@link FlowrSearchGetFilters#id|id} filter:
* `get({id})`.
*/
id(id: NodeId) {
return FlowrSearchGenerator.get({ id });
}
} as const;

export type FlowrSearchBuilderType<Info = NoInfo, ElementType = FlowrSearchElements<Info, FlowrSearchElement<Info>[]>> = FlowrSearchBuilder<Info, ElementType>;

class FlowrSearchBuilder<Info, ElementType = FlowrSearchElements<Info, FlowrSearchElement<Info>[]>> {

Check failure on line 51 in src/search/flowr-search-builder.ts

View workflow job for this annotation

GitHub Actions / 👩‍🏫 Linting (local)

'ElementType' is defined but never used. Allowed unused vars must match /^_/u
private generator: FlowrSearchGeneratorNode;
private search: FlowrSearchTransformerNode[] = [];

constructor(generator: FlowrSearchGeneratorNode) {
this.generator = generator;
}

/**
* TODO
*
* As filter does not change the type of any contained elements, we can return the same type for type safety checks.
*/
filter(filter: FlowrFilterExpression): this {
this.search.push({ type: 'transformer', name: 'filter', args: { filter: filter } });
return this;
}

/**
* first either returns the first element of the search or nothing, if no elements are present.
*/
first(): FlowrSearchBuilder<Info, [FlowrSearchElement<Info>] | []> {
this.search.push({ type: 'transformer', name: 'first', args: undefined });
return this as unknown as FlowrSearchBuilder<Info, [FlowrSearchElement<Info>] | []>;
}

/**
* last either returns the last element of the search or nothing, if no elements are present.
*/
last(): FlowrSearchBuilder<Info, [FlowrSearchElement<Info>] | []> {
this.search.push({ type: 'transformer', name: 'last', args: undefined });
return this as unknown as FlowrSearchBuilder<Info, [FlowrSearchElement<Info>] | []>;
}
/**
* index returns the element at the given index if it exists
*/
index(index: number): FlowrSearchBuilder<Info, [FlowrSearchElement<Info>] | []> {
this.search.push({ type: 'transformer', name: 'index', args: { index } });
return this as unknown as FlowrSearchBuilder<Info, [FlowrSearchElement<Info>] | []>;
}
/**
* tail returns all elements of the search except the first one.
*/
tail(): this {
this.search.push({ type: 'transformer', name: 'tail', args: undefined });
return this;
}

/**
* take returns the first `count` elements of the search.
*/
take(count: number): this {
this.search.push({ type: 'transformer', name: 'take', args: { count } });
return this;
}

/**
* skip returns all elements of the search except the first `count` ones.
*/
skip(count: number): this {
this.search.push({ type: 'transformer', name: 'skip', args: { count } });
return this;
}
}
Empty file.
91 changes: 91 additions & 0 deletions src/search/flowr-search-filters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@


import type { RType } from '../r-bridge/lang-4.x/ast/model/type';
import type { VertexType } from '../dataflow/graph/vertex';

export type FlowrFilterName = keyof typeof FlowrFilters;

export enum FlowrFilter {

}

export const FlowrFilters = {

} as const;


type ValidFilterTypes = FlowrFilterName | RType | VertexType;
/**
* By default, we provide filter for every {@link RType} and {@link VertexType}.
*/
export type FlowrFilterExpression = FlowrFilterCombinator | ValidFilterTypes;

interface BooleanBinaryNode<Composite> {
readonly type: 'and' | 'or' | 'xor';
readonly left: Composite;
readonly right: Composite;
}
interface BooleanUnaryNode<Composite> {
readonly type: 'not';
readonly operand: Composite;
}

type Leaf = ValidFilterTypes;

type BooleanNode = BooleanBinaryNode<BooleanNode>
| BooleanUnaryNode<BooleanNode>
| Leaf;


type BooleanNodeOrCombinator = BooleanNode | FlowrFilterCombinator

export class FlowrFilterCombinator {
private tree: BooleanNode;

protected constructor(init: BooleanNodeOrCombinator) {
this.tree = this.unpack(init);
}

public static is(value: BooleanNodeOrCombinator): FlowrFilterCombinator {
return new this(value);
}

public and(right: BooleanNodeOrCombinator): this {
this.tree = {
type: 'and',
left: this.tree,
right: this.unpack(right)
};
return this;
}

public or(right: BooleanNodeOrCombinator): this {
this.tree = {
type: 'or',
left: this.tree,
right: this.unpack(right)
};
return this;
}

public xor(right: BooleanNodeOrCombinator): this {
this.tree = {
type: 'xor',
left: this.tree,
right: this.unpack(right)
};
return this;
}

public not(): this {
this.tree = {
type: 'not',
operand: this.tree
};
return this;
}

private unpack(val: BooleanNodeOrCombinator): BooleanNode {
return val instanceof FlowrFilterCombinator ? val.tree : val;
}
}
70 changes: 70 additions & 0 deletions src/search/flowr-search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { NoInfo, RNode } from '../r-bridge/lang-4.x/ast/model/model';
import type { Pipeline, PipelineOutput, PipelineStepOutputWithName } from '../core/steps/pipeline/pipeline';
import type { NormalizedAst } from '../r-bridge/lang-4.x/ast/model/processing/decorate';
import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id';
import type { FlowrFilterExpression } from './flowr-search-filters';
import type { DataflowGraph } from '../dataflow/graph/graph';

export interface FlowrSearchElement<Info> {
readonly node: RNode<Info>;
}

export interface FlowrSearchNodeBase<Type extends string, Name extends string, Args extends Record<string, unknown> | undefined> {
readonly type: Type;
readonly name: Name;
readonly args: Args;
}

/* Input extends FlowrSearchElements<Info>, Output extends FlowrSearchElements<Info> = Input */
export type FlowrSearchGeneratorNodeBase<Name extends string, Args extends Record<string, unknown> | undefined> = FlowrSearchNodeBase<'generator', Name, Args>;
export type FlowrSearchTransformerNodeBase<Name extends string, Args extends Record<string, unknown> | undefined> = FlowrSearchNodeBase<'transformer', Name, Args>;

export interface FlowrSearchGetFilters extends Record<string, unknown> {
readonly line?: number;
readonly column?: number;
readonly name?: string;
readonly id?: NodeId;
}

export type FlowrSearchGeneratorNode = FlowrSearchGeneratorNodeBase<'all', undefined>
| FlowrSearchGeneratorNodeBase<'get', FlowrSearchGetFilters>

export type FlowrSearchTransformerNode = FlowrSearchTransformerNodeBase<'first', undefined>
| FlowrSearchTransformerNodeBase<'last', undefined>
| FlowrSearchTransformerNodeBase<'index', { index: number }>
| FlowrSearchTransformerNodeBase<'tail', undefined>
| FlowrSearchTransformerNodeBase<'take', { count: number }>
| FlowrSearchTransformerNodeBase<'skip', { count: number }>
| FlowrSearchTransformerNodeBase<'filter', {
filter: FlowrFilterExpression;
}>

type MinimumInputForFlowrSearch<P extends Pipeline> =
PipelineStepOutputWithName<P, 'normalize'> extends NormalizedAst ? (
PipelineStepOutputWithName<P, 'dataflow'> extends DataflowGraph ? PipelineOutput<P>
: never
): never

/** we allow any pipeline, which provides us with a 'normalize' and 'dataflow' step */
export type FlowrSearchInput<
P extends Pipeline
> = MinimumInputForFlowrSearch<P>

/** Intentionally, we abstract away from an array to avoid the use of conventional typescript operations */
export class FlowrSearchElements<Info = NoInfo, Elements extends FlowrSearchElement<Info>[] = FlowrSearchElement<Info>[]> {
private readonly elements: Elements = [] as unknown as Elements;

public add(this: FlowrSearchElements<Info, Elements>, element: FlowrSearchElement<Info>): FlowrSearchElements<Info, FlowrSearchElement<Info>[]> {
this.elements.push(element);
return this;
}

public getElements(): readonly FlowrSearchElement<Info>[] {
return this.elements;
}
/* TODO: conventional operations */
}

/* TODO: differentiate generators, transformer, and terminators */


18 changes: 18 additions & 0 deletions test/functionality/search/playground.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, test } from 'vitest';
import type { FlowrSearchBuilderType } from '../../../src/search/flowr-search-builder';
import { FlowrSearchGenerator as Q } from '../../../src/search/flowr-search-builder';
import { RType } from '../../../src/r-bridge/lang-4.x/ast/model/type';
import { VertexType } from '../../../src/dataflow/graph/vertex';
import { FlowrFilterCombinator as F } from '../../../src/search/flowr-search-filters';

describe('flowR Search (playground)', () => {
function print(search: FlowrSearchBuilderType) {
console.log(JSON.stringify(search, null, 2));
}
test('poor mans testing', () => {
print(Q.all().filter(RType.Comment));
print(Q.get({ line: 3, name: 'x' }).filter(
F.is(VertexType.Use).or(RType.Number)
).first());
});
});
Loading