Skip to content

Commit

Permalink
feat: init basic flowr search api
Browse files Browse the repository at this point in the history
cause i wanted to think about something different
  • Loading branch information
EagleoutIce committed Nov 24, 2024
1 parent 4313a35 commit f6d2d98
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 0 deletions.
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());
});
});

0 comments on commit f6d2d98

Please sign in to comment.