@eagleoutice/flowr 2.6.0 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -40
- package/abstract-interpretation/data-frame/absint-visitor.d.ts +1 -1
- package/abstract-interpretation/data-frame/absint-visitor.js +1 -1
- package/cli/flowr.js +8 -4
- package/cli/repl/commands/repl-cfg.js +4 -4
- package/cli/repl/commands/repl-commands.js +2 -2
- package/cli/repl/commands/repl-dataflow.js +4 -4
- package/cli/repl/commands/repl-execute.d.ts +1 -1
- package/cli/repl/commands/repl-execute.js +8 -8
- package/cli/repl/commands/repl-lineage.d.ts +2 -2
- package/cli/repl/commands/repl-lineage.js +11 -12
- package/cli/repl/commands/repl-main.d.ts +4 -7
- package/cli/repl/commands/repl-normalize.js +2 -2
- package/cli/repl/commands/repl-parse.js +3 -2
- package/cli/repl/commands/repl-query.js +3 -3
- package/cli/repl/commands/repl-quit.js +1 -1
- package/cli/repl/commands/repl-version.d.ts +1 -16
- package/cli/repl/commands/repl-version.js +2 -19
- package/cli/repl/core.d.ts +9 -9
- package/cli/repl/core.js +14 -20
- package/cli/repl/print-version.js +2 -2
- package/cli/repl/server/connection.js +6 -2
- package/cli/repl/server/messages/message-hello.d.ts +1 -1
- package/cli/repl/server/server.js +2 -2
- package/core/steps/all/core/20-dataflow.d.ts +3 -1
- package/core/steps/pipeline/default-pipelines.d.ts +66 -50
- package/dataflow/environments/default-builtin-config.js +8 -0
- package/dataflow/eval/resolve/alias-tracking.js +2 -0
- package/dataflow/eval/resolve/resolve.js +3 -0
- package/dataflow/eval/values/r-value.d.ts +4 -1
- package/dataflow/eval/values/r-value.js +2 -0
- package/dataflow/extractor.d.ts +4 -1
- package/dataflow/extractor.js +7 -5
- package/dataflow/fn/higher-order-function.d.ts +9 -0
- package/dataflow/fn/higher-order-function.js +75 -0
- package/documentation/doc-util/doc-repl.js +5 -2
- package/documentation/print-dataflow-graph-wiki.js +2 -2
- package/documentation/print-query-wiki.js +20 -0
- package/documentation/print-readme.js +1 -1
- package/package.json +1 -1
- package/project/cache/flowr-analyzer-cache.d.ts +6 -5
- package/project/cache/flowr-analyzer-cache.js +21 -13
- package/project/cfg-kind.d.ts +17 -0
- package/project/cfg-kind.js +22 -0
- package/project/context/abstract-flowr-analyzer-context.d.ts +4 -0
- package/project/context/flowr-analyzer-context.d.ts +6 -0
- package/project/context/flowr-analyzer-context.js +11 -0
- package/project/context/flowr-analyzer-dependencies-context.d.ts +1 -0
- package/project/context/flowr-analyzer-dependencies-context.js +4 -0
- package/project/context/flowr-analyzer-files-context.d.ts +1 -0
- package/project/context/flowr-analyzer-files-context.js +4 -0
- package/project/context/flowr-analyzer-loading-order-context.d.ts +1 -0
- package/project/context/flowr-analyzer-loading-order-context.js +6 -0
- package/project/flowr-analyzer.d.ts +19 -18
- package/project/flowr-analyzer.js +15 -8
- package/queries/catalog/call-context-query/call-context-query-executor.js +2 -1
- package/queries/catalog/config-query/config-query-format.d.ts +1 -1
- package/queries/catalog/control-flow-query/control-flow-query-executor.js +2 -1
- package/queries/catalog/df-shape-query/df-shape-query-executor.js +1 -1
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.d.ts +3 -0
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-executor.js +45 -0
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.d.ts +22 -0
- package/queries/catalog/inspect-higher-order-query/inspect-higher-order-query-format.js +32 -0
- package/queries/query.d.ts +10 -2
- package/queries/query.js +2 -0
- package/r-bridge/parser.d.ts +7 -0
- package/search/search-executor/search-enrichers.js +2 -1
- package/util/r-value.d.ts +1 -1
- package/util/r-value.js +2 -0
- package/util/version.d.ts +17 -0
- package/util/version.js +28 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeHigherOrderQuery = executeHigherOrderQuery;
|
|
4
|
+
const parse_1 = require("../../../slicing/criterion/parse");
|
|
5
|
+
const vertex_1 = require("../../../dataflow/graph/vertex");
|
|
6
|
+
const higher_order_function_1 = require("../../../dataflow/fn/higher-order-function");
|
|
7
|
+
async function executeHigherOrderQuery({ analyzer }, queries) {
|
|
8
|
+
const start = Date.now();
|
|
9
|
+
let filters = undefined;
|
|
10
|
+
// filter will remain undefined if at least one of the queries wants all functions
|
|
11
|
+
for (const q of queries) {
|
|
12
|
+
if (q.filter === undefined) {
|
|
13
|
+
filters = undefined;
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
filters ??= [];
|
|
18
|
+
filters = filters.concat(filters);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const ast = await analyzer.normalize();
|
|
22
|
+
const filterFor = new Set();
|
|
23
|
+
if (filters) {
|
|
24
|
+
for (const f of filters) {
|
|
25
|
+
const i = (0, parse_1.tryResolveSliceCriterionToId)(f, ast.idMap);
|
|
26
|
+
if (i !== undefined) {
|
|
27
|
+
filterFor.add(i);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const graph = (await analyzer.dataflow()).graph;
|
|
32
|
+
const fns = graph.vertices(true)
|
|
33
|
+
.filter(([, v]) => (0, vertex_1.isFunctionDefinitionVertex)(v) && (filterFor.size === 0 || filterFor.has(v.id)));
|
|
34
|
+
const result = {};
|
|
35
|
+
for (const [id,] of fns) {
|
|
36
|
+
result[id] = (0, higher_order_function_1.isHigherOrder)(id, graph);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
'.meta': {
|
|
40
|
+
timing: Date.now() - start
|
|
41
|
+
},
|
|
42
|
+
higherOrder: result
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=inspect-higher-order-query-executor.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BaseQueryFormat, BaseQueryResult } from '../../base-query-format';
|
|
2
|
+
import Joi from 'joi';
|
|
3
|
+
import { executeHigherOrderQuery } from './inspect-higher-order-query-executor';
|
|
4
|
+
import type { NodeId } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id';
|
|
5
|
+
import type { SingleSlicingCriterion } from '../../../slicing/criterion/parse';
|
|
6
|
+
/**
|
|
7
|
+
* Either returns all function definitions alongside whether they are higher-order functions,
|
|
8
|
+
* or just those matching the filters.
|
|
9
|
+
*/
|
|
10
|
+
export interface InspectHigherOrderQuery extends BaseQueryFormat {
|
|
11
|
+
readonly type: 'inspect-higher-order';
|
|
12
|
+
readonly filter?: SingleSlicingCriterion[];
|
|
13
|
+
}
|
|
14
|
+
export interface InspectHigherOrderQueryResult extends BaseQueryResult {
|
|
15
|
+
readonly higherOrder: Record<NodeId, boolean>;
|
|
16
|
+
}
|
|
17
|
+
export declare const InspectHigherOrderQueryDefinition: {
|
|
18
|
+
readonly executor: typeof executeHigherOrderQuery;
|
|
19
|
+
readonly asciiSummarizer: (formatter: import("../../../util/text/ansi").OutputFormatter, processed: import("../../../project/flowr-analyzer").FlowrAnalysisProvider, queryResults: BaseQueryResult, result: string[]) => Promise<boolean>;
|
|
20
|
+
readonly schema: Joi.ObjectSchema<any>;
|
|
21
|
+
readonly flattenInvolvedNodes: (queryResults: BaseQueryResult) => NodeId[];
|
|
22
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.InspectHigherOrderQueryDefinition = void 0;
|
|
7
|
+
const ansi_1 = require("../../../util/text/ansi");
|
|
8
|
+
const joi_1 = __importDefault(require("joi"));
|
|
9
|
+
const inspect_higher_order_query_executor_1 = require("./inspect-higher-order-query-executor");
|
|
10
|
+
const node_id_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/node-id");
|
|
11
|
+
const dfg_1 = require("../../../util/mermaid/dfg");
|
|
12
|
+
exports.InspectHigherOrderQueryDefinition = {
|
|
13
|
+
executor: inspect_higher_order_query_executor_1.executeHigherOrderQuery,
|
|
14
|
+
asciiSummarizer: async (formatter, processed, queryResults, result) => {
|
|
15
|
+
const out = queryResults;
|
|
16
|
+
result.push(`Query: ${(0, ansi_1.bold)('inspect-higher-order', formatter)} (${out['.meta'].timing.toFixed(0)}ms)`);
|
|
17
|
+
for (const [r, v] of Object.entries(out.higherOrder)) {
|
|
18
|
+
const loc = (await processed.normalize()).idMap.get((0, node_id_1.normalizeIdToNumberIfPossible)(r))?.location ?? undefined;
|
|
19
|
+
result.push(` - Function ${(0, ansi_1.bold)(r, formatter)} (${(0, dfg_1.formatRange)(loc)}) is ${v ? '' : 'not '}a higher-order function`);
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
},
|
|
23
|
+
schema: joi_1.default.object({
|
|
24
|
+
type: joi_1.default.string().valid('inspect-higher-order').required().description('The type of the query.'),
|
|
25
|
+
filter: joi_1.default.array().items(joi_1.default.string().required()).optional().description('If given, only function definitions that match one of the given slicing criteria are considered. Each criterion can be either `line:column`, `line@variable-name`, or `$id`, where the latter directly specifies the node id of the function definition to be considered.')
|
|
26
|
+
}).description('Either returns all function definitions alongside whether they are higher-order functions, or just those matching the filters.'),
|
|
27
|
+
flattenInvolvedNodes: (queryResults) => {
|
|
28
|
+
const out = queryResults;
|
|
29
|
+
return Object.keys(out.higherOrder).filter(id => out.higherOrder[id]);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=inspect-higher-order-query-format.js.map
|
package/queries/query.d.ts
CHANGED
|
@@ -25,11 +25,13 @@ import type { ControlFlowQuery } from './catalog/control-flow-query/control-flow
|
|
|
25
25
|
import type { DfShapeQuery } from './catalog/df-shape-query/df-shape-query-format';
|
|
26
26
|
import type { AsyncOrSync } from 'ts-essentials';
|
|
27
27
|
import type { FlowrConfigOptions } from '../config';
|
|
28
|
+
import type { InspectHigherOrderQuery } from './catalog/inspect-higher-order-query/inspect-higher-order-query-format';
|
|
28
29
|
import type { FlowrAnalysisProvider } from '../project/flowr-analyzer';
|
|
29
30
|
/**
|
|
30
31
|
* These are all queries that can be executed from within flowR
|
|
32
|
+
* {@link SynchronousQuery} are queries that can be executed synchronously, i.e., they do not return a Promise.
|
|
31
33
|
*/
|
|
32
|
-
export type Query = CallContextQuery | ConfigQuery | SearchQuery | DataflowQuery | ControlFlowQuery | DataflowLensQuery | DfShapeQuery | NormalizedAstQuery | IdMapQuery | DataflowClusterQuery | StaticSliceQuery | LineageQuery | DependenciesQuery | LocationMapQuery | HappensBeforeQuery | ResolveValueQuery | ProjectQuery | OriginQuery | LinterQuery;
|
|
34
|
+
export type Query = CallContextQuery | ConfigQuery | SearchQuery | DataflowQuery | ControlFlowQuery | DataflowLensQuery | DfShapeQuery | NormalizedAstQuery | IdMapQuery | DataflowClusterQuery | StaticSliceQuery | LineageQuery | DependenciesQuery | LocationMapQuery | HappensBeforeQuery | InspectHigherOrderQuery | ResolveValueQuery | ProjectQuery | OriginQuery | LinterQuery;
|
|
33
35
|
export type QueryArgumentsWithType<QueryType extends BaseQueryFormat['type']> = Query & {
|
|
34
36
|
type: QueryType;
|
|
35
37
|
};
|
|
@@ -57,7 +59,7 @@ export declare const SupportedQueries: {
|
|
|
57
59
|
};
|
|
58
60
|
readonly config: {
|
|
59
61
|
readonly executor: typeof import("./catalog/config-query/config-query-executor").executeConfigQuery;
|
|
60
|
-
readonly asciiSummarizer: (formatter: OutputFormatter, _analyzer: unknown, queryResults: BaseQueryResult, result: string[]) =>
|
|
62
|
+
readonly asciiSummarizer: (formatter: OutputFormatter, _analyzer: unknown, queryResults: BaseQueryResult, result: string[]) => true;
|
|
61
63
|
readonly completer: (partialLine: readonly string[], config: FlowrConfigOptions) => string[];
|
|
62
64
|
readonly fromLine: (line: readonly string[], _config: FlowrConfigOptions) => [ConfigQuery];
|
|
63
65
|
readonly schema: Joi.ObjectSchema<any>;
|
|
@@ -141,6 +143,12 @@ export declare const SupportedQueries: {
|
|
|
141
143
|
readonly schema: Joi.ObjectSchema<any>;
|
|
142
144
|
readonly flattenInvolvedNodes: () => never[];
|
|
143
145
|
};
|
|
146
|
+
readonly 'inspect-higher-order': {
|
|
147
|
+
readonly executor: typeof import("./catalog/inspect-higher-order-query/inspect-higher-order-query-executor").executeHigherOrderQuery;
|
|
148
|
+
readonly asciiSummarizer: (formatter: OutputFormatter, processed: FlowrAnalysisProvider, queryResults: BaseQueryResult, result: string[]) => Promise<boolean>;
|
|
149
|
+
readonly schema: Joi.ObjectSchema<any>;
|
|
150
|
+
readonly flattenInvolvedNodes: (queryResults: BaseQueryResult) => NodeId[];
|
|
151
|
+
};
|
|
144
152
|
readonly 'resolve-value': {
|
|
145
153
|
readonly executor: typeof import("./catalog/resolve-value-query/resolve-value-query-executor").executeResolveValueQuery;
|
|
146
154
|
readonly asciiSummarizer: (formatter: OutputFormatter, _analyzer: FlowrAnalysisProvider, queryResults: BaseQueryResult, result: string[]) => true;
|
package/queries/query.js
CHANGED
|
@@ -32,6 +32,7 @@ const origin_query_format_1 = require("./catalog/origin-query/origin-query-forma
|
|
|
32
32
|
const linter_query_format_1 = require("./catalog/linter-query/linter-query-format");
|
|
33
33
|
const control_flow_query_format_1 = require("./catalog/control-flow-query/control-flow-query-format");
|
|
34
34
|
const df_shape_query_format_1 = require("./catalog/df-shape-query/df-shape-query-format");
|
|
35
|
+
const inspect_higher_order_query_format_1 = require("./catalog/inspect-higher-order-query/inspect-higher-order-query-format");
|
|
35
36
|
exports.SupportedQueries = {
|
|
36
37
|
'call-context': call_context_query_format_1.CallContextQueryDefinition,
|
|
37
38
|
'config': config_query_format_1.ConfigQueryDefinition,
|
|
@@ -48,6 +49,7 @@ exports.SupportedQueries = {
|
|
|
48
49
|
'location-map': location_map_query_format_1.LocationMapQueryDefinition,
|
|
49
50
|
'search': search_query_format_1.SearchQueryDefinition,
|
|
50
51
|
'happens-before': happens_before_query_format_1.HappensBeforeQueryDefinition,
|
|
52
|
+
'inspect-higher-order': inspect_higher_order_query_format_1.InspectHigherOrderQueryDefinition,
|
|
51
53
|
'resolve-value': resolve_value_query_format_1.ResolveValueQueryDefinition,
|
|
52
54
|
'project': project_query_format_1.ProjectQueryDefinition,
|
|
53
55
|
'origin': origin_query_format_1.OriginQueryDefinition,
|
package/r-bridge/parser.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ interface ParserContent<T> {
|
|
|
8
8
|
parse(request: RParseRequest): T;
|
|
9
9
|
close(): void;
|
|
10
10
|
}
|
|
11
|
+
export interface TreeSitterInformation {
|
|
12
|
+
name: TreeSitterExecutor['name'];
|
|
13
|
+
}
|
|
14
|
+
export interface RShellInformation {
|
|
15
|
+
name: RShell['name'];
|
|
16
|
+
rVersion: string;
|
|
17
|
+
}
|
|
11
18
|
export type SyncParser<T> = ParserContent<Awaited<T>> & {
|
|
12
19
|
readonly async?: false;
|
|
13
20
|
};
|
|
@@ -12,6 +12,7 @@ const dfg_get_origin_1 = require("../../dataflow/origin/dfg-get-origin");
|
|
|
12
12
|
const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id");
|
|
13
13
|
const cfg_simplification_1 = require("../../control-flow/cfg-simplification");
|
|
14
14
|
const call_context_query_executor_1 = require("../../queries/catalog/call-context-query/call-context-query-executor");
|
|
15
|
+
const cfg_kind_1 = require("../../project/cfg-kind");
|
|
15
16
|
/**
|
|
16
17
|
* An enumeration that stores the names of the available enrichments that can be applied to a set of search elements.
|
|
17
18
|
* See {@link FlowrSearchBuilder.with} for more information on how to apply enrichments.
|
|
@@ -113,7 +114,7 @@ exports.Enrichments = {
|
|
|
113
114
|
}
|
|
114
115
|
const content = {
|
|
115
116
|
...prev,
|
|
116
|
-
cfg: await data.controlflow(args.simplificationPasses,
|
|
117
|
+
cfg: await data.controlflow(args.simplificationPasses, cfg_kind_1.CfgKind.WithDataflow),
|
|
117
118
|
};
|
|
118
119
|
if (args.checkReachable) {
|
|
119
120
|
content.reachableNodes = (0, cfg_simplification_1.cfgFindAllReachable)(content.cfg);
|
package/util/r-value.d.ts
CHANGED
|
@@ -20,4 +20,4 @@ export declare function unliftRValue(value: ValueString): RStringValue | undefin
|
|
|
20
20
|
export declare function unliftRValue(value: ValueNumber | ValueInterval): RNumberValue | undefined;
|
|
21
21
|
export declare function unliftRValue(value: ValueLogical): RLogicalValue | undefined;
|
|
22
22
|
export declare function unliftRValue(value: ValueVector): (RStringValue | RNumberValue | RLogicalValue)[] | undefined;
|
|
23
|
-
export declare function unliftRValue(value: Value): RStringValue | RNumberValue | boolean | (RStringValue | RNumberValue | RLogicalValue)[] | undefined;
|
|
23
|
+
export declare function unliftRValue(value: Value): RStringValue | RNumberValue | 'fn-def' | boolean | ('fn-def' | RStringValue | RNumberValue | RLogicalValue)[] | undefined;
|
package/util/r-value.js
CHANGED
package/util/version.d.ts
CHANGED
|
@@ -1,2 +1,19 @@
|
|
|
1
1
|
import { SemVer } from 'semver';
|
|
2
|
+
import type { KnownParser } from '../r-bridge/parser';
|
|
3
|
+
import type { ReplOutput } from '../cli/repl/commands/repl-main';
|
|
4
|
+
import type { FlowrAnalysisProvider } from '../project/flowr-analyzer';
|
|
2
5
|
export declare function flowrVersion(): SemVer;
|
|
6
|
+
type Version = `${number}.${number}.${number}`;
|
|
7
|
+
/**
|
|
8
|
+
* Describes the version of flowR and the used R interpreter.
|
|
9
|
+
*/
|
|
10
|
+
export interface VersionInformation {
|
|
11
|
+
/** The version of flowR */
|
|
12
|
+
flowr: Version;
|
|
13
|
+
/** The version of R identified by the underlying {@link RShell} */
|
|
14
|
+
r: Version | 'unknown' | 'none';
|
|
15
|
+
engine: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function retrieveVersionInformation(input: KnownParser | FlowrAnalysisProvider): Promise<VersionInformation>;
|
|
18
|
+
export declare function printVersionInformation(output: ReplOutput, input: KnownParser | FlowrAnalysisProvider): Promise<void>;
|
|
19
|
+
export {};
|
package/util/version.js
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.flowrVersion = flowrVersion;
|
|
4
|
+
exports.retrieveVersionInformation = retrieveVersionInformation;
|
|
5
|
+
exports.printVersionInformation = printVersionInformation;
|
|
4
6
|
const semver_1 = require("semver");
|
|
7
|
+
const assert_1 = require("./assert");
|
|
5
8
|
// this is automatically replaced with the current version by release-it
|
|
6
|
-
const version = '2.6.
|
|
9
|
+
const version = '2.6.1';
|
|
7
10
|
function flowrVersion() {
|
|
8
11
|
return new semver_1.SemVer(version);
|
|
9
12
|
}
|
|
13
|
+
const versionRegex = /^\d+\.\d+\.\d+/m;
|
|
14
|
+
async function retrieveVersionInformation(input) {
|
|
15
|
+
const flowr = flowrVersion().toString();
|
|
16
|
+
let r;
|
|
17
|
+
let name;
|
|
18
|
+
if ('name' in input) {
|
|
19
|
+
r = await input.rVersion();
|
|
20
|
+
name = input.name;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const parserInformation = await input.parserInformation();
|
|
24
|
+
r = parserInformation.name === 'r-shell' ? parserInformation.rVersion : 'unknown';
|
|
25
|
+
name = parserInformation.name;
|
|
26
|
+
}
|
|
27
|
+
(0, assert_1.guard)(versionRegex.test(flowr), `flowR version ${flowr} does not match the expected format!`);
|
|
28
|
+
(0, assert_1.guard)(r === 'unknown' || r === 'none' || versionRegex.test(r), `R version ${r} does not match the expected format!`);
|
|
29
|
+
return { flowr: flowr, r: r, engine: name };
|
|
30
|
+
}
|
|
31
|
+
async function printVersionInformation(output, input) {
|
|
32
|
+
const { flowr, r, engine } = await retrieveVersionInformation(input);
|
|
33
|
+
output.stdout(`Engine: ${engine}`);
|
|
34
|
+
output.stdout(` flowR: ${flowr}`);
|
|
35
|
+
output.stdout(` R: ${r}`);
|
|
36
|
+
}
|
|
10
37
|
//# sourceMappingURL=version.js.map
|