From 2745e6743c8a3abf772f908deedf85f09fefcb57 Mon Sep 17 00:00:00 2001 From: JordanBoltonMN Date: Tue, 7 Apr 2020 21:20:05 -0700 Subject: [PATCH] Scope rework (#124) * initial commit * wip on scope2 * big WIP * first test up and failing as expected * working towards imp filter fn * might remove scopeUtils * impl EachExpression almost done * one failing test * fixed scope propagation * impl FunctionExpression * impl Record * WIP on Section impl * fixing bugs, next is LetExpression * partial impl for LetExpression * finished LetExpression impl * working on updating to v2 inspection * working on scope/inspection v2 * it builds and tests pass, still needs refactoring * removed dead code * lots of minor changes * prettier format * rename * more renaming, updated tryInspection * updated tryLexParseInspect * created top level Task export * updated inspection example * updated task usage example * updated readme link --- README.md | 2 +- src/example.ts | 23 +- src/index.ts | 4 +- src/inspection/activeNode/activeNodeUtils.ts | 78 -- src/inspection/autocomplete.ts | 51 +- src/inspection/index.ts | 5 +- src/inspection/inspection.ts | 74 -- src/inspection/inspectionUtils.ts | 56 -- .../{scope => }/invokeExpression.ts | 90 +- src/inspection/scope/identifier.ts | 426 --------- src/inspection/scope/index.ts | 2 - src/inspection/scope/scope.ts | 435 ++++++++- src/inspection/scope/scopeItem.ts | 32 +- src/inspection/type.ts | 38 +- src/parser/nodeIdMap/ancestryUtils.ts | 87 ++ src/parser/nodeIdMap/index.ts | 3 +- src/parser/nodeIdMap/nodeIdMapIterator.ts | 158 +-- src/settings.ts | 5 +- src/{tasks.ts => task.ts} | 90 +- src/test/common.ts | 63 +- src/test/fileUtils.ts | 8 +- .../libraryTest/inspection/autocomplete.ts | 347 ++++--- .../inspection/invokeExpression.ts | 145 +++ src/test/libraryTest/inspection/scope.ts | 904 ++++++++++++++++++ .../libraryTest/inspection/scope/ancestry.ts | 218 ----- .../inspection/scope/identifier.ts | 818 ---------------- .../inspection/scope/invokeExpression.ts | 128 --- .../libraryTest/inspection/scope/parameter.ts | 76 -- .../inspection/scope/tokenRange.ts | 12 - .../inspection/{scope => }/type.ts | 64 +- src/test/libraryTest/parser/children.ts | 4 +- src/test/libraryTest/parser/simple.ts | 6 +- src/test/resourceTest/benchmark.ts | 11 +- src/test/resourceTest/lexParse.ts | 4 +- .../inspector/functionExpressionInspector.ts | 2 +- 35 files changed, 2091 insertions(+), 2378 deletions(-) delete mode 100644 src/inspection/inspection.ts delete mode 100644 src/inspection/inspectionUtils.ts rename src/inspection/{scope => }/invokeExpression.ts (65%) delete mode 100644 src/inspection/scope/identifier.ts create mode 100644 src/parser/nodeIdMap/ancestryUtils.ts rename src/{tasks.ts => task.ts} (59%) create mode 100644 src/test/libraryTest/inspection/invokeExpression.ts create mode 100644 src/test/libraryTest/inspection/scope.ts delete mode 100644 src/test/libraryTest/inspection/scope/ancestry.ts delete mode 100644 src/test/libraryTest/inspection/scope/identifier.ts delete mode 100644 src/test/libraryTest/inspection/scope/invokeExpression.ts delete mode 100644 src/test/libraryTest/inspection/scope/parameter.ts delete mode 100644 src/test/libraryTest/inspection/scope/tokenRange.ts rename src/test/libraryTest/inspection/{scope => }/type.ts (55%) diff --git a/README.md b/README.md index fe41b652..a673e783 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A parser for the [Power Query/M](https://docs.microsoft.com/en-us/power-query/) ## How to use -The most common way to consume the project is to interact with the helper functions found in [src/tasks.ts](src/tasks.ts). There are all-in-one functions, such as `tryLexParseInspection`, which does a full pass on a given document. There are also incremental functions, such as `tryLex` and `tryParse`, which perform one step at a time. Minimal code samples can be found in [example.ts](src/example.ts). +The most common way to consume the project is to interact with the helper functions found in [src/task.ts](src/task.ts). There are all-in-one functions, such as `tryLexParseInspection`, which does a full pass on a given document. There are also incremental functions, such as `tryLex` and `tryParse`, which perform one step at a time. Minimal code samples can be found in [example.ts](src/example.ts). ## Things to note diff --git a/src/example.ts b/src/example.ts index 5ea90dfd..f413a7f0 100644 --- a/src/example.ts +++ b/src/example.ts @@ -3,12 +3,11 @@ /* tslint:disable:no-console */ -import { Inspection } from "."; +import { Task } from "."; import { ResultUtils } from "./common"; import { Lexer, LexError, LexerSnapshot, TriedLexerSnapshot } from "./lexer"; import { ParseError } from "./parser"; import { DefaultSettings } from "./settings"; -import { TriedLexParse, TriedLexParseInspection, tryLexParse, tryLexParseInspection } from "./tasks"; parseText(`if true then 1 else 2`); @@ -16,7 +15,7 @@ parseText(`if true then 1 else 2`); function parseText(text: string): void { // Try lexing and parsing the argument which returns a Result object. // A Result is the union (Ok | Err). - const triedLexParse: TriedLexParse = tryLexParse(DefaultSettings, text); + const triedLexParse: Task.TriedLexParse = Task.tryLexParse(DefaultSettings, text); // If the Result is an Ok, then dump the jsonified abstract syntax tree (AST) which was parsed. if (ResultUtils.isOk(triedLexParse)) { @@ -102,12 +101,24 @@ function lexText(text: string): void { function inspectText(text: string, position: Inspection.Position): void { // Having a LexError thrown will abort the inspection and return the offending LexError. // So long as a TriedParse is created from reaching the parsing stage then an inspection will be returned. - const triedInspection: TriedLexParseInspection = tryLexParseInspection(DefaultSettings, text, position); + const triedInspection: Task.TriedLexParseInspect = Task.tryLexParseInspection(DefaultSettings, text, position); if (ResultUtils.isErr(triedInspection)) { console.log(`Inspection failed due to: ${triedInspection.error.message}`); return; } - const inspected: Inspection.Inspected = triedInspection.value; + const inspection: Task.InspectionOk = triedInspection.value; - console.log(`Inspected scope: ${[...inspected.scope.entries()]}`); + for (const identifier of inspection.scope.keys()) { + console.log(`Identifier: ${identifier} has type ${inspection.scopeType.get(identifier)!.kind}`); + } + + console.log(`Suggested keyword autocomplete: ${inspection.autocomplete.join(", ")}`); + + console.log(`InvokeExpression name: ${inspection.maybeInvokeExpression?.maybeName}`); + console.log( + `InvokeExpression number of arguments: ${inspection.maybeInvokeExpression?.maybeArguments?.numArguments}`, + ); + console.log( + `InvokeExpression argument position: ${inspection.maybeInvokeExpression?.maybeArguments?.positionArgumentIndex}`, + ); } diff --git a/src/index.ts b/src/index.ts index 8869f647..3b53dd03 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,12 +2,12 @@ // Licensed under the MIT license. import * as Inspection from "./inspection"; +import * as Task from "./task"; -export { Inspection }; +export { Inspection, Task }; export * from "./common"; export * from "./lexer"; export * from "./localization"; export * from "./parser"; export * from "./settings"; -export * from "./tasks"; export * from "./type"; diff --git a/src/inspection/activeNode/activeNodeUtils.ts b/src/inspection/activeNode/activeNodeUtils.ts index d05d97f1..d6cb2710 100644 --- a/src/inspection/activeNode/activeNodeUtils.ts +++ b/src/inspection/activeNode/activeNodeUtils.ts @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { CommonError } from "../../common"; import { Ast, AstUtils, @@ -64,83 +63,6 @@ export function maybeActiveNode( }; } -export function expectRoot(activeNode: ActiveNode): TXorNode { - const ancestry: ReadonlyArray = activeNode.ancestry; - return ancestry[ancestry.length - 1]; -} - -export function expectLeaf(activeNode: ActiveNode): TXorNode { - return activeNode.ancestry[0]; -} - -export function maybePreviousXorNode( - activeNode: ActiveNode, - ancestorIndex: number, - n: number = 1, - maybeNodeKinds: ReadonlyArray | undefined = undefined, -): TXorNode | undefined { - const maybeXorNode: TXorNode | undefined = activeNode.ancestry[ancestorIndex - n]; - if (maybeXorNode !== undefined && maybeNodeKinds !== undefined) { - return maybeNodeKinds.indexOf(maybeXorNode.node.kind) !== -1 ? maybeXorNode : undefined; - } else { - return maybeXorNode; - } -} - -export function maybeNextXorNode(activeNode: ActiveNode, ancestorIndex: number, n: number = 1): TXorNode | undefined { - return activeNode.ancestry[ancestorIndex + n]; -} - -export function expectPreviousXorNode( - activeNode: ActiveNode, - ancestorIndex: number, - n: number = 1, - maybeAllowedNodeKinds: ReadonlyArray | undefined = undefined, -): TXorNode { - const maybeXorNode: TXorNode | undefined = maybePreviousXorNode(activeNode, ancestorIndex, n); - if (maybeXorNode === undefined) { - throw new CommonError.InvariantError("no previous node"); - } - const xorNode: TXorNode = maybeXorNode; - - if (maybeAllowedNodeKinds !== undefined) { - const maybeErr: CommonError.InvariantError | undefined = NodeIdMapUtils.testAstAnyNodeKind( - xorNode, - maybeAllowedNodeKinds, - ); - if (maybeErr) { - throw maybeErr; - } - } - - return maybeXorNode; -} - -export function expectNextXorNode( - activeNode: ActiveNode, - ancestorIndex: number, - n: number = 1, - maybeAllowedNodeKinds: ReadonlyArray | undefined = undefined, -): TXorNode { - const maybeXorNode: TXorNode | undefined = maybeNextXorNode(activeNode, ancestorIndex, n); - if (maybeXorNode === undefined) { - throw new CommonError.InvariantError("no next node"); - } - const xorNode: TXorNode = maybeXorNode; - - if (maybeAllowedNodeKinds !== undefined) { - const maybeErr: CommonError.InvariantError | undefined = NodeIdMapUtils.testAstAnyNodeKind( - xorNode, - maybeAllowedNodeKinds, - ); - if (maybeErr) { - throw maybeErr; - } - } - - return maybeXorNode; -} - interface AstNodeSearch { readonly maybeNode: Ast.TNode | undefined; readonly maybeShiftedNode: Ast.TNode | undefined; diff --git a/src/inspection/autocomplete.ts b/src/inspection/autocomplete.ts index 11dbc03d..4f7fb465 100644 --- a/src/inspection/autocomplete.ts +++ b/src/inspection/autocomplete.ts @@ -4,27 +4,32 @@ import { CommonError, Result } from "../common"; import { ResultUtils } from "../common/result"; import { KeywordKind, TExpressionKeywords, Token, TokenKind } from "../lexer"; -import { Ast, IParserState, NodeIdMap, NodeIdMapUtils, ParseError, TXorNode, XorNodeKind } from "../parser"; -import { InspectionSettings } from "../settings"; -import { ActiveNode, ActiveNodeUtils } from "./activeNode"; +import { + AncestryUtils, + Ast, + IParserState, + NodeIdMap, + NodeIdMapUtils, + ParseError, + TXorNode, + XorNodeKind, +} from "../parser"; +import { CommonSettings } from "../settings"; +import { ActiveNode } from "./activeNode"; import { Position, PositionUtils } from "./position"; -export interface InspectedAutocomplete { - readonly autocompleteKeywords: ReadonlyArray; -} +export type Autocomplete = ReadonlyArray; -export type TriedAutocomplete = Result; +export type TriedAutocomplete = Result; -export function tryInspectAutocomplete( - settings: InspectionSettings, +export function tryAutocomplete( + settings: CommonSettings, maybeActiveNode: ActiveNode | undefined, nodeIdMapCollection: NodeIdMap.Collection, maybeParseError: ParseError.ParseError | undefined, ): TriedAutocomplete { if (maybeActiveNode === undefined) { - return ResultUtils.okFactory({ - autocompleteKeywords: ExpressionAutocomplete, - }); + return ResultUtils.okFactory(ExpressionAutocomplete); } const activeNode: ActiveNode = maybeActiveNode; @@ -65,7 +70,7 @@ export function tryInspectAutocomplete( ); inspected = filterRecommendations(inspected, maybePositionName); - return ResultUtils.okFactory({ autocompleteKeywords: inspected }); + return ResultUtils.okFactory(inspected); } // Travel the ancestry path in Active node in [parent, child] pairs. @@ -73,7 +78,7 @@ export function tryInspectAutocomplete( // For example 'if true |' gives us a pair something like [IfExpression, Constant]. // We can now know we failed to parse a 'then' constant. function traverseAncestors( - settings: InspectionSettings, + settings: CommonSettings, activeNode: ActiveNode, nodeIdMapCollection: NodeIdMap.Collection, maybeParseErrorToken: Token | undefined, @@ -338,7 +343,7 @@ function autocompleteErrorHandlingExpression( function autocompleteListExpression( activeNode: ActiveNode, child: TXorNode, - ancestorIndex: number, + ancestryIndex: number, ): ReadonlyArray | undefined { // '{' or '}' if (child.node.maybeAttributeIndex === 0 || child.node.maybeAttributeIndex === 2) { @@ -352,7 +357,7 @@ function autocompleteListExpression( } // ListExpression -> ArrayWrapper -> Csv -> X - const nodeOrComma: TXorNode = ActiveNodeUtils.expectPreviousXorNode(activeNode, ancestorIndex, 3, undefined); + const nodeOrComma: TXorNode = AncestryUtils.expectPreviousXorNode(activeNode.ancestry, ancestryIndex, 3, undefined); if (nodeOrComma.node.maybeAttributeIndex !== 0) { return undefined; } @@ -361,7 +366,7 @@ function autocompleteListExpression( // but we have to drill down one more level if it's a RangeExpression. const itemNode: TXorNode = nodeOrComma.node.kind === Ast.NodeKind.RangeExpression - ? ActiveNodeUtils.expectPreviousXorNode(activeNode, ancestorIndex, 4, undefined) + ? AncestryUtils.expectPreviousXorNode(activeNode.ancestry, ancestryIndex, 4, undefined) : nodeOrComma; if (itemNode.kind === XorNodeKind.Context || PositionUtils.isBeforeXorNode(activeNode.position, itemNode, false)) { @@ -378,7 +383,7 @@ function autocompleteSectionMember( activeNode: ActiveNode, parent: TXorNode, child: TXorNode, - ancestorIndex: number, + ancestryIndex: number, ): ReadonlyArray | undefined { // SectionMember.namePairedExpression if (child.node.maybeAttributeIndex === 2) { @@ -395,10 +400,12 @@ function autocompleteSectionMember( } // SectionMember -> IdentifierPairedExpression -> Identifier - const maybeName: TXorNode | undefined = ActiveNodeUtils.maybePreviousXorNode(activeNode, ancestorIndex, 2, [ - Ast.NodeKind.IdentifierPairedExpression, - Ast.NodeKind.Identifier, - ]); + const maybeName: TXorNode | undefined = AncestryUtils.maybePreviousXorNode( + activeNode.ancestry, + ancestryIndex, + 2, + [Ast.NodeKind.IdentifierPairedExpression, Ast.NodeKind.Identifier], + ); // Name hasn't been parsed yet so we can exit. if (maybeName === undefined || maybeName.kind !== XorNodeKind.Ast) { diff --git a/src/inspection/index.ts b/src/inspection/index.ts index 630ced0f..61167593 100644 --- a/src/inspection/index.ts +++ b/src/inspection/index.ts @@ -1,11 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import * as InspectionUtils from "./inspectionUtils"; - -export { InspectionUtils }; export * from "./autocomplete"; -export * from "./inspection"; export * from "./position"; export * from "./scope"; export * from "./type"; +export * from "./invokeExpression"; diff --git a/src/inspection/inspection.ts b/src/inspection/inspection.ts deleted file mode 100644 index 19f96d90..00000000 --- a/src/inspection/inspection.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { CommonError, Result, ResultUtils } from "../common"; -import { IParserState, NodeIdMap, ParseError } from "../parser"; -import { InspectionSettings } from "../settings"; -import { tryInspectAutocomplete, tryInspectScope, tryInspectScopeType } from "./"; -import { ActiveNode, ActiveNodeUtils } from "./activeNode"; -import { InspectedAutocomplete, TriedAutocomplete as TriedInspectAutocomplete } from "./autocomplete"; -import { Position } from "./position"; -import { InspectedScope, TriedInspectScope } from "./scope"; -import { InspectedType, TriedType as TriedInspectType } from "./type"; - -// Inspection is designed to run sub-inspections, -// eg. one inspection for scope and one for keywords. -// Look in `state.ts` to see the traversal and return types for each sub-inspection. -// If any sub-inspection returns an Err, return the Err. -// If all sub-inspections succeed, return the union of all successful traversals. - -export interface InspectedCommon { - readonly maybeActiveNode: ActiveNode | undefined; -} -export type Inspected = InspectedCommon & InspectedScope & InspectedAutocomplete & InspectedType; -export type TriedInspection = Result; - -export function tryFrom( - settings: InspectionSettings, - position: Position, - nodeIdMapCollection: NodeIdMap.Collection, - leafNodeIds: ReadonlyArray, - maybeParseError: ParseError.ParseError | undefined, -): TriedInspection { - const maybeActiveNode: ActiveNode | undefined = ActiveNodeUtils.maybeActiveNode( - position, - nodeIdMapCollection, - leafNodeIds, - ); - - const triedInspectScope: TriedInspectScope = tryInspectScope( - settings, - maybeActiveNode, - nodeIdMapCollection, - leafNodeIds, - ); - if (ResultUtils.isErr(triedInspectScope)) { - return triedInspectScope; - } - - const triedInspectType: TriedInspectType = tryInspectScopeType( - settings, - triedInspectScope.value, - nodeIdMapCollection, - ); - if (ResultUtils.isErr(triedInspectType)) { - return triedInspectType; - } - - const triedInspectAutocomplete: TriedInspectAutocomplete = tryInspectAutocomplete( - settings, - maybeActiveNode, - nodeIdMapCollection, - maybeParseError, - ); - if (ResultUtils.isErr(triedInspectAutocomplete)) { - return triedInspectAutocomplete; - } - - return ResultUtils.okFactory({ - maybeActiveNode, - ...triedInspectScope.value, - ...triedInspectAutocomplete.value, - ...triedInspectType.value, - }); -} diff --git a/src/inspection/inspectionUtils.ts b/src/inspection/inspectionUtils.ts deleted file mode 100644 index 2728af60..00000000 --- a/src/inspection/inspectionUtils.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { CommonError } from "../common"; -import { Ast, TXorNode } from "../parser"; -import { ActiveNode, ActiveNodeUtils } from "./activeNode"; - -export interface InspectionState { - readonly nodeIndex: number; - readonly activeNode: ActiveNode; -} - -// Should only be called: RecordLiteral, RecordExpression, SectionMember -export function isInKeyValuePairAssignment(state: InspectionState): boolean { - // How far back do we look to find a paired expression? - // - // For SectionMember it's a single indirection, eg: - // 'X -> KeyValuePair' - // - // For everything else it's 3, where the extra 2 come from an array of Csvs, eg: - // 'Current -> ArrayWrapper -> Csv -> KeyValuePair' - let n: number; - if (state.activeNode.ancestry[state.nodeIndex].node.kind === Ast.NodeKind.SectionMember) { - n = 1; - } else { - n = 3; - } - - const maybeKeyValuePair: TXorNode | undefined = ActiveNodeUtils.maybePreviousXorNode( - state.activeNode, - state.nodeIndex, - n, - [ - Ast.NodeKind.GeneralizedIdentifierPairedAnyLiteral, - Ast.NodeKind.GeneralizedIdentifierPairedExpression, - Ast.NodeKind.IdentifierPairedExpression, - Ast.NodeKind.IdentifierExpressionPairedExpression, - ], - ); - if (maybeKeyValuePair === undefined) { - return false; - } - const keyValuePair: TXorNode = maybeKeyValuePair; - - const ancestry: ReadonlyArray = state.activeNode.ancestry; - - const keyValuePairAncestryIndex: number = ancestry.indexOf(keyValuePair); - if (keyValuePairAncestryIndex === -1) { - throw new CommonError.InvariantError("xorNode isn't in ancestry"); - } - - const maybeChild: TXorNode | undefined = ancestry[keyValuePairAncestryIndex - 1]; - if (maybeChild === undefined) { - const details: {} = { keyValuePairId: keyValuePair.node.id }; - throw new CommonError.InvariantError("expected xorNode to have a child", details); - } - - return maybeChild.node.maybeAttributeIndex === 2; -} diff --git a/src/inspection/scope/invokeExpression.ts b/src/inspection/invokeExpression.ts similarity index 65% rename from src/inspection/scope/invokeExpression.ts rename to src/inspection/invokeExpression.ts index 9755ab4f..6129c99c 100644 --- a/src/inspection/scope/invokeExpression.ts +++ b/src/inspection/invokeExpression.ts @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { CommonError } from "../../common"; -import { Ast, NodeIdMap, NodeIdMapIterator, NodeIdMapUtils, TXorNode, XorNodeKind } from "../../parser"; -import { ActiveNode, ActiveNodeUtils } from "../activeNode"; -import { Position, PositionUtils } from "../position"; +import { CommonError, Result, ResultUtils } from "../common"; +import { AncestryUtils, Ast, NodeIdMap, NodeIdMapIterator, NodeIdMapUtils, TXorNode, XorNodeKind } from "../parser"; +import { CommonSettings } from "../settings"; +import { ActiveNode } from "./activeNode"; +import { Position, PositionUtils } from "./position"; -export interface InspectedInvokeExpression { - readonly maybeInvokeExpression: InvokeExpression | undefined; -} +export type InspectedInvokeExpression = undefined | InvokeExpression; + +export type TriedInvokeExpression = Result; export interface InvokeExpression { readonly xorNode: TXorNode; @@ -21,31 +22,34 @@ export interface InvokeExpressionArgs { readonly positionArgumentIndex: number; } -export function inspectInvokeExpression( - activeNode: ActiveNode, +export function tryInvokeExpression( + settings: CommonSettings, nodeIdMapCollection: NodeIdMap.Collection, -): InspectedInvokeExpression { + activeNode: ActiveNode, +): TriedInvokeExpression { const ancestors: ReadonlyArray = activeNode.ancestry; const numAncestors: number = activeNode.ancestry.length; const position: Position = activeNode.position; - for (let index: number = 0; index < numAncestors; index += 1) { - const xorNode: TXorNode = ancestors[index]; - if (!isInvokeExpressionContent(position, xorNode)) { - continue; - } - return { - maybeInvokeExpression: { + try { + for (let ancestryIndex: number = 0; ancestryIndex < numAncestors; ancestryIndex += 1) { + const xorNode: TXorNode = ancestors[ancestryIndex]; + if (!isInvokeExpressionContent(position, xorNode)) { + continue; + } + + const inspected: InspectedInvokeExpression = { xorNode: xorNode, maybeName: maybeInvokeExpressionName(nodeIdMapCollection, xorNode.node.id), - maybeArguments: inspectInvokeExpressionArguments(nodeIdMapCollection, activeNode, index), - }, - }; + maybeArguments: inspectInvokeExpressionArguments(nodeIdMapCollection, activeNode, ancestryIndex), + }; + return ResultUtils.okFactory(inspected); + } + } catch (err) { + return ResultUtils.errFactory(CommonError.ensureCommonError(settings.localizationTemplates, err)); } - return { - maybeInvokeExpression: undefined, - }; + return ResultUtils.okFactory(undefined); } function isInvokeExpressionContent(position: Position, xorNode: TXorNode): boolean { @@ -65,24 +69,23 @@ function isInvokeExpressionContent(position: Position, xorNode: TXorNode): boole } function maybeInvokeExpressionName(nodeIdMapCollection: NodeIdMap.Collection, nodeId: number): string | undefined { - const invokeExprXorNode: TXorNode = NodeIdMapUtils.expectXorNode(nodeIdMapCollection, nodeId); - - if (invokeExprXorNode.node.kind !== Ast.NodeKind.InvokeExpression) { - const details: {} = { invokeExprXorNode }; - throw new CommonError.InvariantError( - `expected invokeExprXorNode to have a Ast.NodeKind of ${Ast.NodeKind.InvokeExpression}`, - details, - ); + const invokeExpr: TXorNode = NodeIdMapUtils.expectXorNode(nodeIdMapCollection, nodeId); + const maybeErr: undefined | CommonError.InvariantError = NodeIdMapUtils.testAstNodeKind( + invokeExpr, + Ast.NodeKind.InvokeExpression, + ); + if (maybeErr) { + throw maybeErr; } // The only place for an identifier in a RecursivePrimaryExpression is as the head, therefore an InvokeExpression // only has a name if the InvokeExpression is the 0th element in the RecursivePrimaryExpressionArray. let maybeName: string | undefined; - if (invokeExprXorNode.node.maybeAttributeIndex === 0) { + if (invokeExpr.node.maybeAttributeIndex === 0) { // Grab the RecursivePrimaryExpression's head if it's an IdentifierExpression const recursiveArrayXorNode: TXorNode = NodeIdMapUtils.expectParentXorNode( nodeIdMapCollection, - invokeExprXorNode.node.id, + invokeExpr.node.id, ); const recursiveExprXorNode: TXorNode = NodeIdMapUtils.expectParentXorNode( nodeIdMapCollection, @@ -98,10 +101,10 @@ function maybeInvokeExpressionName(nodeIdMapCollection: NodeIdMap.Collection, no if (headXorNode.kind !== XorNodeKind.Ast) { const details: {} = { identifierExpressionNodeId: headXorNode.node.id, - invokeExpressionNodeId: invokeExprXorNode.node.id, + invokeExpressionNodeId: invokeExpr.node.id, }; throw new CommonError.InvariantError( - `the younger IdentifierExpression sibling should've finished parsing before the InvokeExpression node was reached`, + `${maybeInvokeExpressionName.name}: the younger IdentifierExpression sibling should've finished parsing before the InvokeExpression node was reached`, details, ); } @@ -124,24 +127,29 @@ function inspectInvokeExpressionArguments( nodeIndex: number, ): InvokeExpressionArgs | undefined { // Grab arguments if they exist, else return early. - const maybeCsvArray: TXorNode | undefined = ActiveNodeUtils.maybePreviousXorNode(activeNode, nodeIndex, 1, [ + const maybeCsvArray: TXorNode | undefined = AncestryUtils.maybePreviousXorNode(activeNode.ancestry, nodeIndex, 1, [ Ast.NodeKind.ArrayWrapper, ]); if (maybeCsvArray === undefined) { return undefined; } - // const nodeIdMapCollection: NodeIdMap.Collection = state.nodeIdMapCollection; - // const position: Position = state.activeNode.position; + const csvArray: TXorNode = maybeCsvArray; const csvNodes: ReadonlyArray = NodeIdMapIterator.expectXorChildren( nodeIdMapCollection, csvArray.node.id, ); const numArguments: number = csvNodes.length; + if (numArguments === 0) { + return undefined; + } - const maybeAncestorCsv: TXorNode | undefined = ActiveNodeUtils.maybePreviousXorNode(activeNode, nodeIndex, 2, [ - Ast.NodeKind.Csv, - ]); + const maybeAncestorCsv: TXorNode | undefined = AncestryUtils.maybePreviousXorNode( + activeNode.ancestry, + nodeIndex, + 2, + [Ast.NodeKind.Csv], + ); const maybePositionArgumentIndex: number | undefined = maybeAncestorCsv !== undefined ? maybeAncestorCsv.node.maybeAttributeIndex : undefined; diff --git a/src/inspection/scope/identifier.ts b/src/inspection/scope/identifier.ts deleted file mode 100644 index f1556208..00000000 --- a/src/inspection/scope/identifier.ts +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { ScopeItemKind, TScopeItem } from "."; -import { InspectionUtils } from ".."; -import { CommonError, isNever, Result, ResultKind } from "../../common"; -import { Ast, NodeIdMap, NodeIdMapIterator, NodeIdMapUtils, TXorNode, XorNodeKind } from "../../parser"; -import { InspectionSettings } from "../../settings"; -import { ActiveNode, ActiveNodeUtils } from "../activeNode"; -import { Position, PositionUtils } from "../position"; -import { TypeInspector, TypeUtils } from "../../type"; - -// The inspection travels across ActiveNode.ancestry to build up a scope. -export interface InspectedIdentifier { - readonly scope: ReadonlyMap; -} - -export function tryInspectIdentifier( - settings: InspectionSettings, - activeNode: ActiveNode, - nodeIdMapCollection: NodeIdMap.Collection, - leafNodeIds: ReadonlyArray, -): Result { - const state: IdentifierState = { - nodeIndex: 0, - result: { - scope: new Map(), - }, - activeNode, - nodeIdMapCollection, - leafNodeIds, - }; - - try { - const ancestry: ReadonlyArray = activeNode.ancestry; - const numNodes: number = ancestry.length; - for (let index: number = 0; index < numNodes; index += 1) { - state.nodeIndex = index; - const xorNode: TXorNode = ancestry[index]; - inspectNode(state, xorNode); - } - - return { - kind: ResultKind.Ok, - value: { - ...state.result, - }, - }; - } catch (err) { - return { - kind: ResultKind.Err, - error: CommonError.ensureCommonError(settings.localizationTemplates, err), - }; - } -} - -interface IdentifierState { - nodeIndex: number; - result: InspectedIdentifier; - readonly activeNode: ActiveNode; - readonly nodeIdMapCollection: NodeIdMap.Collection; - readonly leafNodeIds: ReadonlyArray; -} - -function inspectNode(state: IdentifierState, xorNode: TXorNode): void { - switch (xorNode.node.kind) { - case Ast.NodeKind.EachExpression: - inspectEachExpression(state, xorNode); - break; - - case Ast.NodeKind.FunctionExpression: - inspectFunctionExpression(state, xorNode); - break; - - case Ast.NodeKind.Identifier: - inspectIdentifier(state, xorNode, true); - break; - - case Ast.NodeKind.IdentifierExpression: - inspectIdentifierExpression(state, xorNode, true); - break; - - case Ast.NodeKind.IdentifierPairedExpression: - break; - - case Ast.NodeKind.LetExpression: - inspectLetExpression(state, xorNode); - break; - - case Ast.NodeKind.RecordExpression: - case Ast.NodeKind.RecordLiteral: - inspectRecordExpressionOrRecordLiteral(state, xorNode); - break; - - case Ast.NodeKind.SectionMember: - inspectSectionMember(state, xorNode); - break; - - default: - break; - } -} - -// If you came from the TExpression in the EachExpression, -// then add '_' to the scope. -function inspectEachExpression(state: IdentifierState, eachExpr: TXorNode): void { - const previous: TXorNode = ActiveNodeUtils.expectPreviousXorNode(state.activeNode, state.nodeIndex); - if (previous.node.maybeAttributeIndex !== 1) { - return; - } - - mightUpdateScope(state, "_", { - kind: ScopeItemKind.Each, - each: eachExpr, - }); -} - -// If position is to the right of '=>', -// then add all parameter names to the scope. -function inspectFunctionExpression(state: IdentifierState, fnExpr: TXorNode): void { - if (fnExpr.node.kind !== Ast.NodeKind.FunctionExpression) { - throw expectedNodeKindError(fnExpr, Ast.NodeKind.FunctionExpression); - } - - // We only care about parameters if we're to the right of the '=>' - const previous: TXorNode = ActiveNodeUtils.expectPreviousXorNode(state.activeNode, state.nodeIndex); - if (previous.node.maybeAttributeIndex !== 3) { - return; - } - - const inspectedFnExpr: TypeInspector.InspectedFunctionExpression = TypeInspector.inspectFunctionExpression( - state.nodeIdMapCollection, - fnExpr, - ); - - inspectedFnExpr.parameters.map((parameter: TypeInspector.InspectedFunctionParameter) => { - mightUpdateScope(state, parameter.name.literal, { - kind: ScopeItemKind.Parameter, - name: parameter.name, - isOptional: parameter.isOptional, - isNullable: parameter.isNullable, - maybeType: - parameter.maybeType !== undefined - ? TypeUtils.maybePrimitiveTypeConstantKindFromTypeKind(parameter.maybeType) - : undefined, - }); - }); -} - -function inspectIdentifier(state: IdentifierState, identifier: TXorNode, isRoot: boolean): void { - // Ignore the case of a Context node as there are two possible states: - // An empty context (no children), or an Ast.TNode instance. - // Both have no identifier attached to it. - // - // Ignore the case of where the parent is an IdentifierExpression as the parent handle adding to the scope. - if (identifier.kind !== XorNodeKind.Ast || isParentOfNodeKind(state, Ast.NodeKind.IdentifierExpression)) { - return; - } - - if (identifier.node.kind !== Ast.NodeKind.Identifier) { - throw expectedNodeKindError(identifier, Ast.NodeKind.Identifier); - } - const identifierAstNode: Ast.Identifier = identifier.node; - - // Don't add the identifier to scope if it's the root and position is before the identifier starts. - // 'a +| b' - // '|foo' - const position: Position = state.activeNode.position; - if (isRoot && PositionUtils.isBeforeAstNode(position, identifierAstNode, true)) { - return; - } - - // Don't add the identifier if you're coming from inside a ParameterList - // '(foo|, bar) => 1' - const maybeNext: TXorNode | undefined = ActiveNodeUtils.maybeNextXorNode(state.activeNode, state.nodeIndex); - if (maybeNext && maybeNext.node.kind === Ast.NodeKind.Parameter) { - return; - } - - mightUpdateScope(state, identifierAstNode.literal, { - kind: ScopeItemKind.Undefined, - xorNode: identifier, - }); -} - -function inspectIdentifierExpression(state: IdentifierState, identifierExpr: TXorNode, isLeaf: boolean): void { - // Don't add the identifier to scope if it's the leaf, - // and if the position is before the start of the identifier. - // 'a +| b' - // '|foo' - if (isLeaf && PositionUtils.isBeforeXorNode(state.activeNode.position, identifierExpr, false)) { - return; - } - - let key: string; - switch (identifierExpr.kind) { - case XorNodeKind.Ast: { - if (identifierExpr.node.kind !== Ast.NodeKind.IdentifierExpression) { - throw expectedNodeKindError(identifierExpr, Ast.NodeKind.IdentifierExpression); - } - - const identifierExprAstNode: Ast.IdentifierExpression = identifierExpr.node; - const identifier: Ast.Identifier = identifierExprAstNode.identifier; - const maybeInclusiveConstant: Ast.IConstant | undefined = - identifierExprAstNode.maybeInclusiveConstant; - - key = - maybeInclusiveConstant !== undefined - ? maybeInclusiveConstant.constantKind + identifier.literal - : identifier.literal; - break; - } - - case XorNodeKind.Context: { - key = ""; - const nodeIdMapCollection: NodeIdMap.Collection = state.nodeIdMapCollection; - - // Add the optional inclusive constant `@` if it was parsed. - const maybeInclusiveConstant: - | TXorNode - | undefined = NodeIdMapUtils.maybeXorChildByAttributeIndex( - nodeIdMapCollection, - identifierExpr.node.id, - 0, - [Ast.NodeKind.Constant], - ); - if (maybeInclusiveConstant !== undefined) { - const inclusiveConstant: Ast.IConstant = maybeInclusiveConstant.node as Ast.IConstant< - Ast.MiscConstantKind.AtSign - >; - // Adds the '@' prefix. - key = inclusiveConstant.constantKind; - } - - const maybeIdentifier: - | TXorNode - | undefined = NodeIdMapUtils.maybeXorChildByAttributeIndex( - nodeIdMapCollection, - identifierExpr.node.id, - 1, - [Ast.NodeKind.Identifier], - ); - if (maybeIdentifier !== undefined) { - const identifier: Ast.Identifier = maybeIdentifier.node as Ast.Identifier; - key += identifier.literal; - } - break; - } - - default: - throw isNever(identifierExpr); - } - - if (key.length) { - mightUpdateScope(state, key, { - kind: ScopeItemKind.Undefined, - xorNode: identifierExpr, - }); - } -} - -// If position is to the right of an equals sign, -// then add all keys to the scope EXCEPT for the key that the position is under. -function inspectLetExpression(state: IdentifierState, letExpr: TXorNode): void { - const maybePreviousAttributeIndex: number | undefined = ActiveNodeUtils.expectPreviousXorNode( - state.activeNode, - state.nodeIndex, - ).node.maybeAttributeIndex; - if (maybePreviousAttributeIndex !== 3 && !InspectionUtils.isInKeyValuePairAssignment(state)) { - return; - } - - const nodeIdMapCollection: NodeIdMap.Collection = state.nodeIdMapCollection; - - let maybeAncestorKeyValuePair: TXorNode | undefined; - // If ancestor is an expression - if (maybePreviousAttributeIndex === 3) { - maybeAncestorKeyValuePair = undefined; - } else { - maybeAncestorKeyValuePair = ActiveNodeUtils.expectPreviousXorNode(state.activeNode, state.nodeIndex, 3, [ - Ast.NodeKind.IdentifierPairedExpression, - ]); - } - - for (const kvp of NodeIdMapIterator.letKeyValuePairs(nodeIdMapCollection, letExpr)) { - if (maybeAncestorKeyValuePair && maybeAncestorKeyValuePair.node.id === kvp.source.node.id) { - continue; - } - - const keyValuePairId: number = kvp.source.node.id; - const maybeKey: Ast.Identifier | undefined = NodeIdMapUtils.maybeAstChildByAttributeIndex( - nodeIdMapCollection, - keyValuePairId, - 0, - [Ast.NodeKind.Identifier], - ) as Ast.Identifier; - if (maybeKey === undefined) { - continue; - } - const key: Ast.Identifier = maybeKey; - const maybeValue: TXorNode | undefined = NodeIdMapUtils.maybeXorChildByAttributeIndex( - nodeIdMapCollection, - keyValuePairId, - 2, - undefined, - ); - mightUpdateScope(state, key.literal, { - kind: ScopeItemKind.KeyValuePair, - key, - maybeValue, - }); - } -} - -// If position is to the right of an equals sign, -// then add all keys to scope EXCEPT for the one the that position is under. -function inspectRecordExpressionOrRecordLiteral(state: IdentifierState, record: TXorNode): void { - const nodeIdMapCollection: NodeIdMap.Collection = state.nodeIdMapCollection; - - // Only add to scope if you're in the right hand of an assignment. - if (!InspectionUtils.isInKeyValuePairAssignment(state)) { - return; - } - - const ancestorKeyValuePair: TXorNode = ActiveNodeUtils.expectPreviousXorNode(state.activeNode, state.nodeIndex, 3, [ - Ast.NodeKind.GeneralizedIdentifierPairedAnyLiteral, - Ast.NodeKind.GeneralizedIdentifierPairedExpression, - ]); - - for (const kvp of NodeIdMapIterator.recordKeyValuePairs(nodeIdMapCollection, record)) { - if (kvp.source.node.id === ancestorKeyValuePair.node.id) { - continue; - } - - mightUpdateScope(state, kvp.keyLiteral, { - kind: ScopeItemKind.KeyValuePair, - key: kvp.key, - maybeValue: kvp.maybeValue, - }); - } -} - -function inspectSectionMember(state: IdentifierState, sectionMember: TXorNode): void { - if (!InspectionUtils.isInKeyValuePairAssignment(state)) { - return; - } - - const nodeIdMapCollection: NodeIdMap.Collection = state.nodeIdMapCollection; - const sectionMemberArray: TXorNode = ActiveNodeUtils.expectNextXorNode(state.activeNode, state.nodeIndex, 1, [ - Ast.NodeKind.ArrayWrapper, - ]); - const sectionMembers: ReadonlyArray = NodeIdMapIterator.expectXorChildren( - nodeIdMapCollection, - sectionMemberArray.node.id, - ); - for (const iterSectionMember of sectionMembers) { - // Ignore if it's the current SectionMember. - if (iterSectionMember.node.id === sectionMember.node.id) { - continue; - } - - const maybeKeyValuePair: - | TXorNode - | undefined = NodeIdMapUtils.maybeXorChildByAttributeIndex( - nodeIdMapCollection, - iterSectionMember.node.id, - 2, - [Ast.NodeKind.IdentifierPairedExpression], - ); - if (maybeKeyValuePair === undefined) { - continue; - } - const keyValuePair: TXorNode = maybeKeyValuePair; - - // Add name to scope. - const maybeName: TXorNode | undefined = NodeIdMapUtils.maybeXorChildByAttributeIndex( - nodeIdMapCollection, - keyValuePair.node.id, - 0, - [Ast.NodeKind.Identifier], - ); - if (maybeName === undefined || maybeName.kind === XorNodeKind.Context) { - continue; - } - const name: Ast.Identifier = maybeName.node as Ast.Identifier; - - const maybeValue: TXorNode | undefined = NodeIdMapUtils.maybeXorChildByAttributeIndex( - nodeIdMapCollection, - keyValuePair.node.id, - 2, - undefined, - ); - - mightUpdateScope(state, name.literal, { - kind: ScopeItemKind.SectionMember, - key: name, - maybeValue, - }); - } -} - -function expectedNodeKindError(xorNode: TXorNode, expected: Ast.NodeKind): CommonError.InvariantError { - const details: {} = { - xorNodeId: xorNode.node.id, - expectedNodeKind: expected, - actualNodeKind: xorNode.node.kind, - }; - return new CommonError.InvariantError(`expected xorNode to be of kind ${expected}`, details); -} - -function isParentOfNodeKind(state: IdentifierState, parentNodeKind: Ast.NodeKind): boolean { - const maybeParent: TXorNode | undefined = ActiveNodeUtils.maybeNextXorNode(state.activeNode, state.nodeIndex); - return maybeParent !== undefined ? maybeParent.node.kind === parentNodeKind : false; -} - -function mightUpdateScope(state: IdentifierState, key: string, scopeItem: TScopeItem): void { - const unsafeScope: Map = state.result.scope as Map; - const maybeScopeItem: TScopeItem | undefined = unsafeScope.get(key); - const isUpdateNeeded: boolean = - maybeScopeItem === undefined || - (maybeScopeItem.kind === ScopeItemKind.Undefined && scopeItem.kind !== ScopeItemKind.Undefined); - - if (isUpdateNeeded) { - unsafeScope.set(key, scopeItem); - } -} diff --git a/src/inspection/scope/index.ts b/src/inspection/scope/index.ts index 07ea3d6d..9815b96a 100644 --- a/src/inspection/scope/index.ts +++ b/src/inspection/scope/index.ts @@ -1,7 +1,5 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -export * from "./identifier"; -export * from "./invokeExpression"; export * from "./scope"; export * from "./scopeItem"; diff --git a/src/inspection/scope/scope.ts b/src/inspection/scope/scope.ts index 2fcf4192..633af5f1 100644 --- a/src/inspection/scope/scope.ts +++ b/src/inspection/scope/scope.ts @@ -1,62 +1,413 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { CommonError, Result, ResultKind, ResultUtils } from "../../common"; -import { NodeIdMap } from "../../parser"; -import { InspectionSettings } from "../../settings"; -import { ActiveNode } from "../activeNode"; -import { InspectedIdentifier, tryInspectIdentifier } from "./identifier"; -import { InspectedInvokeExpression, inspectInvokeExpression } from "./invokeExpression"; +import { CommonError, Result, ResultUtils } from "../../common"; +import { Ast, NodeIdMap, NodeIdMapIterator, NodeIdMapUtils, TXorNode } from "../../parser"; +import { CommonSettings } from "../../settings"; +import { TypeInspector, TypeUtils } from "../../type"; +import { + KeyValuePairScopeItem, + ParameterScopeItem, + ScopeItemKind, + SectionMemberScopeItem, + TScopeItem2, +} from "./scopeItem"; -export type InspectedScope = InspectedIdentifier & InspectedInvokeExpression; +export type TriedScope = Result; -export type TriedInspectScope = Result; +export type TriedScopeForRoot = Result; -export function tryInspectScope( - settings: InspectionSettings, - maybeActiveNode: ActiveNode | undefined, +export type ScopeById = Map; + +export type ScopeItemByKey = Map; + +export function tryScope( + settings: CommonSettings, nodeIdMapCollection: NodeIdMap.Collection, leafNodeIds: ReadonlyArray, -): TriedInspectScope { - if (maybeActiveNode === undefined) { - return { - kind: ResultKind.Ok, - value: emptyScopeFactory(), - }; + ancestry: ReadonlyArray, + // If a map is given, then it's mutated and returned. Else create and return a new instance. + maybeScopeById: undefined | ScopeById, +): TriedScope { + const rootId: number = ancestry[0].node.id; + + let scopeById: ScopeById; + if (maybeScopeById !== undefined) { + const maybeCached: undefined | ScopeItemByKey = maybeScopeById.get(rootId); + if (maybeCached !== undefined) { + return ResultUtils.okFactory(maybeScopeById); + } + scopeById = maybeScopeById; + } else { + scopeById = new Map(); } - const activeNode: ActiveNode = maybeActiveNode; + + // Store the delta between the given scope and what's found in a temporary map. + // This will prevent mutation in the given map if an error is thrown. + const scopeChanges: ScopeById = new Map(); + const state: ScopeInspectionState = { + settings, + givenScope: scopeById, + deltaScope: scopeChanges, + ancestry, + nodeIdMapCollection, + leafNodeIds, + ancestryIndex: 0, + }; try { - const triedInspectIdentifier: Result = tryInspectIdentifier( - settings, - maybeActiveNode, - nodeIdMapCollection, - leafNodeIds, + // Build up the scope through a top-down inspection. + const numNodes: number = ancestry.length; + for (let ancestryIndex: number = numNodes - 1; ancestryIndex >= 0; ancestryIndex -= 1) { + state.ancestryIndex = ancestryIndex; + const xorNode: TXorNode = ancestry[ancestryIndex]; + + inspectNode(state, xorNode); + } + + return ResultUtils.okFactory(state.deltaScope); + } catch (err) { + return ResultUtils.errFactory(CommonError.ensureCommonError(state.settings.localizationTemplates, err)); + } +} + +export function tryScopeForRoot( + settings: CommonSettings, + nodeIdMapCollection: NodeIdMap.Collection, + leafNodeIds: ReadonlyArray, + ancestry: ReadonlyArray, + // If a map is given, then it's mutated and returned. Else create and return a new instance. + maybeScopeById: undefined | ScopeById, +): TriedScopeForRoot { + const rootId: number = ancestry[0].node.id; + const triedScopeInspection: TriedScope = tryScope( + settings, + nodeIdMapCollection, + leafNodeIds, + ancestry, + maybeScopeById, + ); + + if (ResultUtils.isErr(triedScopeInspection)) { + return triedScopeInspection; + } + + const maybeScope: undefined | ScopeItemByKey = triedScopeInspection.value.get(rootId); + if (maybeScope === undefined) { + const details: {} = { rootId }; + throw new CommonError.InvariantError( + `${tryScopeForRoot.name}: expected rootId in ${tryScope.name} result`, + details, + ); + } + + return ResultUtils.okFactory(maybeScope); +} + +interface ScopeInspectionState { + readonly settings: CommonSettings; + readonly givenScope: ScopeById; + readonly deltaScope: ScopeById; + readonly ancestry: ReadonlyArray; + readonly nodeIdMapCollection: NodeIdMap.Collection; + readonly leafNodeIds: ReadonlyArray; + ancestryIndex: number; +} + +function inspectNode(state: ScopeInspectionState, xorNode: TXorNode): void { + switch (xorNode.node.kind) { + case Ast.NodeKind.EachExpression: + inspectEachExpression(state, xorNode); + break; + + case Ast.NodeKind.FunctionExpression: + inspectFunctionExpression(state, xorNode); + break; + + case Ast.NodeKind.LetExpression: + inspectLetExpression(state, xorNode); + break; + + case Ast.NodeKind.RecordExpression: + case Ast.NodeKind.RecordLiteral: + inspectRecordExpressionOrRecordLiteral(state, xorNode); + break; + + case Ast.NodeKind.Section: + inspectSection(state, xorNode); + break; + + default: + getOrCreateScope(state, xorNode.node.id, undefined); + break; + } +} + +function inspectEachExpression(state: ScopeInspectionState, eachExpr: TXorNode): void { + const maybeErr: undefined | CommonError.InvariantError = NodeIdMapUtils.testAstNodeKind( + eachExpr, + Ast.NodeKind.EachExpression, + ); + if (maybeErr) { + throw maybeErr; + } + + expandChildScope( + state, + eachExpr, + [1], + [ + [ + "_", + { + kind: ScopeItemKind.Each, + eachExpression: eachExpr, + }, + ], + ], + undefined, + ); +} + +function inspectFunctionExpression(state: ScopeInspectionState, fnExpr: TXorNode): void { + const maybeErr: undefined | CommonError.InvariantError = NodeIdMapUtils.testAstNodeKind( + fnExpr, + Ast.NodeKind.FunctionExpression, + ); + if (maybeErr) { + throw maybeErr; + } + + // Propegates the parent's scope. + const scope: ScopeItemByKey = getOrCreateScope(state, fnExpr.node.id, undefined); + + const inspectedFnExpr: TypeInspector.InspectedFunctionExpression = TypeInspector.inspectFunctionExpression( + state.nodeIdMapCollection, + fnExpr, + ); + + const newEntries: ReadonlyArray<[string, ParameterScopeItem]> = inspectedFnExpr.parameters.map( + (parameter: TypeInspector.InspectedFunctionParameter) => { + return [ + parameter.name.literal, + { + kind: ScopeItemKind.Parameter, + name: parameter.name, + isOptional: parameter.isOptional, + isNullable: parameter.isNullable, + maybeType: + parameter.maybeType !== undefined + ? TypeUtils.maybePrimitiveTypeConstantKindFromTypeKind(parameter.maybeType) + : undefined, + }, + ]; + }, + ); + expandChildScope(state, fnExpr, [3], newEntries, scope); +} + +function inspectLetExpression(state: ScopeInspectionState, letExpr: TXorNode): void { + const maybeErr: undefined | CommonError.InvariantError = NodeIdMapUtils.testAstNodeKind( + letExpr, + Ast.NodeKind.LetExpression, + ); + if (maybeErr) { + throw maybeErr; + } + + // Propegates the parent's scope. + const scope: ScopeItemByKey = getOrCreateScope(state, letExpr.node.id, undefined); + + const keyValuePairs: ReadonlyArray> = NodeIdMapIterator.letKeyValuePairs(state.nodeIdMapCollection, letExpr); + const newEntries: ReadonlyArray<[string, KeyValuePairScopeItem]> = inspectKeyValuePairs( + state, + scope, + keyValuePairs, + ); + expandChildScope(state, letExpr, [3], newEntries, scope); +} + +function inspectRecordExpressionOrRecordLiteral(state: ScopeInspectionState, record: TXorNode): void { + const maybeErr: undefined | CommonError.InvariantError = NodeIdMapUtils.testAstAnyNodeKind(record, [ + Ast.NodeKind.RecordExpression, + Ast.NodeKind.RecordLiteral, + ]); + if (maybeErr) { + throw maybeErr; + } + + // Propegates the parent's scope. + const scope: ScopeItemByKey = getOrCreateScope(state, record.node.id, undefined); + + const keyValuePairs: ReadonlyArray> = NodeIdMapIterator.recordKeyValuePairs(state.nodeIdMapCollection, record); + inspectKeyValuePairs(state, scope, keyValuePairs); +} + +function inspectSection(state: ScopeInspectionState, section: TXorNode): void { + const maybeErr: undefined | CommonError.InvariantError = NodeIdMapUtils.testAstNodeKind( + section, + Ast.NodeKind.Section, + ); + if (maybeErr) { + throw maybeErr; + } + + const keyValuePairs: ReadonlyArray> = NodeIdMapIterator.sectionMemberKeyValuePairs(state.nodeIdMapCollection, section); + const unfilteredNewEntries: ReadonlyArray<[string, SectionMemberScopeItem]> = keyValuePairs.map( + (kvp: NodeIdMapIterator.KeyValuePair) => { + return [ + kvp.key.literal, + { + kind: ScopeItemKind.SectionMember, + key: kvp.key, + maybeValue: kvp.maybeValue, + }, + ]; + }, + ); + + for (const kvp of keyValuePairs) { + if (kvp.maybeValue === undefined) { + continue; + } + + const filteredNewEntries: ReadonlyArray<[string, SectionMemberScopeItem]> = unfilteredNewEntries.filter( + (pair: [string, SectionMemberScopeItem]) => { + return pair[1].key.id !== kvp.key.id; + }, ); - if (ResultUtils.isErr(triedInspectIdentifier)) { - return triedInspectIdentifier; + expandScope(state, kvp.maybeValue, filteredNewEntries, new Map()); + } +} + +function inspectKeyValuePairs( + state: ScopeInspectionState, + parentScope: ScopeItemByKey, + keyValuePairs: ReadonlyArray>, +): ReadonlyArray<[string, KeyValuePairScopeItem]> { + const unfilteredNewEntries: ReadonlyArray<[string, KeyValuePairScopeItem]> = keyValuePairs.map( + (kvp: NodeIdMapIterator.KeyValuePair) => { + return [ + kvp.key.literal, + { + kind: ScopeItemKind.KeyValuePair, + key: kvp.key, + maybeValue: kvp.maybeValue, + }, + ]; + }, + ); + + for (const kvp of keyValuePairs) { + if (kvp.maybeValue === undefined) { + continue; } - const maybeInspectedInvokeExpression: InspectedInvokeExpression = inspectInvokeExpression( - activeNode, - nodeIdMapCollection, + const filteredNewEntries: ReadonlyArray<[string, KeyValuePairScopeItem]> = unfilteredNewEntries.filter( + (pair: [string, KeyValuePairScopeItem]) => { + return pair[1].key.id !== kvp.key.id; + }, ); + expandScope(state, kvp.maybeValue, filteredNewEntries, parentScope); + } - return ResultUtils.okFactory({ - ...triedInspectIdentifier.value, - ...maybeInspectedInvokeExpression, - }); - } catch (err) { - return { - kind: ResultKind.Err, - error: CommonError.ensureCommonError(settings.localizationTemplates, err), - }; + return unfilteredNewEntries; +} + +function expandScope( + state: ScopeInspectionState, + xorNode: TXorNode, + newEntries: ReadonlyArray<[string, TScopeItem2]>, + maybeDefaultScope: undefined | ScopeItemByKey, +): void { + const scope: ScopeItemByKey = getOrCreateScope(state, xorNode.node.id, maybeDefaultScope); + for (const [key, value] of newEntries) { + scope.set(key, value); } } -function emptyScopeFactory(): InspectedScope { - return { - scope: new Map(), - maybeInvokeExpression: undefined, - }; +function expandChildScope( + state: ScopeInspectionState, + parent: TXorNode, + childAttributeIds: ReadonlyArray, + newEntries: ReadonlyArray<[string, TScopeItem2]>, + maybeDefaultScope: undefined | ScopeItemByKey, +): void { + const nodeIdMapCollection: NodeIdMap.Collection = state.nodeIdMapCollection; + const parentId: number = parent.node.id; + + // TODO: optimize this + for (const attributeId of childAttributeIds) { + const maybeChild: undefined | TXorNode = NodeIdMapUtils.maybeXorChildByAttributeIndex( + nodeIdMapCollection, + parentId, + attributeId, + undefined, + ); + if (maybeChild !== undefined) { + expandScope(state, maybeChild, newEntries, maybeDefaultScope); + } + } +} + +// Any operation done on a scope should first invoke `scopeFor` for data integrity. +function getOrCreateScope( + state: ScopeInspectionState, + nodeId: number, + maybeDefaultScope: undefined | ScopeItemByKey, +): ScopeItemByKey { + // If scopeFor has already been called then there should be a nodeId in the deltaScope. + const maybeDeltaScope: undefined | ScopeItemByKey = state.deltaScope.get(nodeId); + if (maybeDeltaScope !== undefined) { + return maybeDeltaScope; + } + + // If given a scope with an existing value then assume it's valid. + // Cache and return. + const maybeGivenScope: undefined | ScopeItemByKey = state.givenScope.get(nodeId); + if (maybeGivenScope !== undefined) { + state.deltaScope.set(nodeId, { ...maybeGivenScope }); + return maybeGivenScope; + } + + if (maybeDefaultScope !== undefined) { + const shallowCopy: ScopeItemByKey = new Map(maybeDefaultScope.entries()); + state.deltaScope.set(nodeId, shallowCopy); + return shallowCopy; + } + + // Default to a parent's scope if the node has a parent. + const maybeParent: undefined | TXorNode = NodeIdMapUtils.maybeParentXorNode( + state.nodeIdMapCollection, + nodeId, + undefined, + ); + if (maybeParent !== undefined) { + const parentNodeId: number = maybeParent.node.id; + + const maybeParentDeltaScope: undefined | ScopeItemByKey = state.deltaScope.get(parentNodeId); + if (maybeParentDeltaScope !== undefined) { + const shallowCopy: ScopeItemByKey = new Map(maybeParentDeltaScope.entries()); + state.deltaScope.set(nodeId, shallowCopy); + return shallowCopy; + } + + const maybeParentGivenScope: undefined | ScopeItemByKey = state.givenScope.get(parentNodeId); + if (maybeParentGivenScope !== undefined) { + const shallowCopy: ScopeItemByKey = new Map(maybeParentGivenScope.entries()); + state.deltaScope.set(nodeId, shallowCopy); + return shallowCopy; + } + } + + // The node has no parent or it hasn't been visited. + const newScope: ScopeItemByKey = new Map(); + state.deltaScope.set(nodeId, newScope); + return newScope; } diff --git a/src/inspection/scope/scopeItem.ts b/src/inspection/scope/scopeItem.ts index de25eb1f..b06841d9 100644 --- a/src/inspection/scope/scopeItem.ts +++ b/src/inspection/scope/scopeItem.ts @@ -1,9 +1,9 @@ -import { Ast, TXorNode } from "../../parser"; - // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -export type TScopeItem = +import { Ast, TXorNode } from "../../parser"; + +export type TScopeItem2 = | EachScopeItem | KeyValuePairScopeItem | ParameterScopeItem @@ -22,12 +22,25 @@ export interface IScopeItem { readonly kind: ScopeItemKind; } +export interface EachScopeItem extends IScopeItem { + readonly kind: ScopeItemKind.Each; + readonly eachExpression: TXorNode; +} + export interface KeyValuePairScopeItem extends IScopeItem { readonly kind: ScopeItemKind.KeyValuePair; readonly key: Ast.Identifier | Ast.GeneralizedIdentifier; readonly maybeValue: TXorNode | undefined; } +export interface ParameterScopeItem extends IScopeItem { + readonly kind: ScopeItemKind.Parameter; + readonly name: Ast.Identifier; + readonly isOptional: boolean; + readonly isNullable: boolean; + readonly maybeType: Ast.PrimitiveTypeConstantKind | undefined; +} + export interface SectionMemberScopeItem extends IScopeItem { readonly kind: ScopeItemKind.SectionMember; readonly key: Ast.Identifier; @@ -38,16 +51,3 @@ export interface UndefinedScopeItem extends IScopeItem { readonly kind: ScopeItemKind.Undefined; readonly xorNode: TXorNode; } - -export interface EachScopeItem extends IScopeItem { - readonly kind: ScopeItemKind.Each; - readonly each: TXorNode; -} - -export interface ParameterScopeItem extends IScopeItem { - readonly kind: ScopeItemKind.Parameter; - readonly name: Ast.Identifier; - readonly isOptional: boolean; - readonly isNullable: boolean; - readonly maybeType: Ast.PrimitiveTypeConstantKind | undefined; -} diff --git a/src/inspection/type.ts b/src/inspection/type.ts index 3120f401..2e728bb3 100644 --- a/src/inspection/type.ts +++ b/src/inspection/type.ts @@ -1,59 +1,47 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { CommonError, isNever, Result, ResultKind } from "../common"; +import { CommonError, isNever, Result, ResultUtils } from "../common"; import { Ast, AstUtils, NodeIdMap, NodeIdMapIterator, NodeIdMapUtils, TXorNode, XorNodeKind } from "../parser"; -import { InspectionSettings } from "../settings"; +import { CommonSettings } from "../settings"; import { Type, TypeUtils } from "../type"; -import { InspectedScope, ScopeItemKind, TScopeItem } from "./scope"; +import { ScopeItemByKey, ScopeItemKind, TScopeItem2 } from "./scope"; export type ScopeTypeMap = Map; -export interface InspectedType { - readonly scopeTypeMap: ScopeTypeMap; -} - -export type TriedType = Result; +export type TriedScopeType = Result; -export function tryInspectScopeType( - settings: InspectionSettings, - inspectedScope: InspectedScope, +export function tryScopeType( + settings: CommonSettings, nodeIdMapCollection: NodeIdMap.Collection, -): Result { + inspectedScope: ScopeItemByKey, +): TriedScopeType { // The return object. Only stores [scope key, TType] pairs. const scopeTypeMap: ScopeTypeMap = new Map(); // A temporary working set. Stores all [nodeId, TType] pairs evaluated. const scopeTypeCacheMap: ScopeTypeCacheMap = new Map(); try { - for (const [key, node] of [...inspectedScope.scope.entries()]) { + for (const [key, node] of [...inspectedScope.entries()]) { scopeTypeMap.set(key, evaluateScopeItem(nodeIdMapCollection, node, scopeTypeCacheMap)); } } catch (err) { - return { - kind: ResultKind.Err, - error: CommonError.ensureCommonError(settings.localizationTemplates, err), - }; + return ResultUtils.errFactory(CommonError.ensureCommonError(settings.localizationTemplates, err)); } - return { - kind: ResultKind.Ok, - value: { - scopeTypeMap, - }, - }; + return ResultUtils.okFactory(scopeTypeMap); } type ScopeTypeCacheMap = Map; function evaluateScopeItem( nodeIdMapCollection: NodeIdMap.Collection, - scopeItem: TScopeItem, + scopeItem: TScopeItem2, scopeTypeMap: ScopeTypeCacheMap, ): Type.TType { switch (scopeItem.kind) { case ScopeItemKind.Each: - return evaluateXorNode(nodeIdMapCollection, scopeTypeMap, scopeItem.each); + return evaluateXorNode(nodeIdMapCollection, scopeTypeMap, scopeItem.eachExpression); case ScopeItemKind.KeyValuePair: return scopeItem.maybeValue === undefined diff --git a/src/parser/nodeIdMap/ancestryUtils.ts b/src/parser/nodeIdMap/ancestryUtils.ts new file mode 100644 index 00000000..5709cc79 --- /dev/null +++ b/src/parser/nodeIdMap/ancestryUtils.ts @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { NodeIdMapUtils } from "."; +import { Ast } from ".."; +import { CommonError } from "../../common"; +import { TXorNode } from "./xorNode"; + +export function expectPreviousXorNode( + ancestry: ReadonlyArray, + ancestryIndex: number, + n: number = 1, + maybeAllowedNodeKinds: ReadonlyArray | undefined = undefined, +): TXorNode { + const maybeXorNode: TXorNode | undefined = maybePreviousXorNode(ancestry, ancestryIndex, n); + if (maybeXorNode === undefined) { + throw new CommonError.InvariantError("no previous node"); + } + const xorNode: TXorNode = maybeXorNode; + + if (maybeAllowedNodeKinds !== undefined) { + const maybeErr: CommonError.InvariantError | undefined = NodeIdMapUtils.testAstAnyNodeKind( + xorNode, + maybeAllowedNodeKinds, + ); + if (maybeErr) { + throw maybeErr; + } + } + + return maybeXorNode; +} + +export function maybePreviousXorNode( + ancestry: ReadonlyArray, + ancestryIndex: number, + n: number = 1, + maybeNodeKinds: ReadonlyArray | undefined = undefined, +): TXorNode | undefined { + const maybeXorNode: TXorNode | undefined = ancestry[ancestryIndex - n]; + if (maybeXorNode !== undefined && maybeNodeKinds !== undefined) { + return maybeNodeKinds.indexOf(maybeXorNode.node.kind) !== -1 ? maybeXorNode : undefined; + } else { + return maybeXorNode; + } +} + +export function expectNextXorNode( + ancestry: ReadonlyArray, + ancestryIndex: number, + n: number = 1, + maybeAllowedNodeKinds: ReadonlyArray | undefined = undefined, +): TXorNode { + const maybeXorNode: TXorNode | undefined = maybeNextXorNode(ancestry, ancestryIndex, n); + if (maybeXorNode === undefined) { + throw new CommonError.InvariantError("no next node"); + } + const xorNode: TXorNode = maybeXorNode; + + if (maybeAllowedNodeKinds !== undefined) { + const maybeErr: CommonError.InvariantError | undefined = NodeIdMapUtils.testAstAnyNodeKind( + xorNode, + maybeAllowedNodeKinds, + ); + if (maybeErr) { + throw maybeErr; + } + } + + return maybeXorNode; +} + +export function maybeNextXorNode( + ancestry: ReadonlyArray, + ancestryIndex: number, + n: number = 1, +): TXorNode | undefined { + return ancestry[ancestryIndex + n]; +} + +export function expectRoot(ancestry: ReadonlyArray): TXorNode { + return ancestry[ancestry.length - 1]; +} + +export function expectLeaf(ancestry: ReadonlyArray): TXorNode { + return ancestry[0]; +} diff --git a/src/parser/nodeIdMap/index.ts b/src/parser/nodeIdMap/index.ts index e10a1af9..1d7aeb75 100644 --- a/src/parser/nodeIdMap/index.ts +++ b/src/parser/nodeIdMap/index.ts @@ -1,6 +1,7 @@ +import * as AncestryUtils from "./ancestryUtils"; import * as NodeIdMap from "./nodeIdMap"; import * as NodeIdMapIterator from "./nodeIdMapIterator"; import * as NodeIdMapUtils from "./nodeIdMapUtils"; -export { NodeIdMap, NodeIdMapIterator, NodeIdMapUtils }; +export { AncestryUtils, NodeIdMap, NodeIdMapIterator, NodeIdMapUtils }; export * from "./xorNode"; diff --git a/src/parser/nodeIdMap/nodeIdMapIterator.ts b/src/parser/nodeIdMap/nodeIdMapIterator.ts index 074e1402..105f6917 100644 --- a/src/parser/nodeIdMap/nodeIdMapIterator.ts +++ b/src/parser/nodeIdMap/nodeIdMapIterator.ts @@ -5,9 +5,9 @@ import { NodeIdMap, NodeIdMapUtils, TXorNode, XorNodeKind } from "."; import { Ast } from ".."; import { CommonError, isNever, MapUtils } from "../../common"; -export interface KeyValuePair { +export interface KeyValuePair { readonly source: TXorNode; - readonly key: Ast.GeneralizedIdentifier | Ast.Identifier; + readonly key: T & (Ast.GeneralizedIdentifier | Ast.Identifier); readonly keyLiteral: string; readonly maybeValue: undefined | TXorNode; } @@ -104,73 +104,10 @@ export function expectXorChildren( return expectXorNodes(nodeIdMapCollection, childIds); } -export function sectionNameValuePairs( - nodeIdMapCollection: NodeIdMap.Collection, - section: TXorNode, -): ReadonlyArray { - const maybeErr: undefined | CommonError.InvariantError = NodeIdMapUtils.testAstNodeKind( - section, - Ast.NodeKind.Section, - ); - if (maybeErr !== undefined) { - throw maybeErr; - } - - const maybeSectionMembers: undefined | TXorNode = NodeIdMapUtils.maybeXorChildByAttributeIndex( - nodeIdMapCollection, - section.node.id, - 4, - [Ast.NodeKind.ArrayWrapper], - ); - if (maybeSectionMembers === undefined) { - return []; - } - - const partial: KeyValuePair[] = []; - for (const sectionMember of arrayWrapperXorNodes(nodeIdMapCollection, maybeSectionMembers)) { - const maybeKeyValuePair: - | TXorNode - | undefined = NodeIdMapUtils.maybeXorChildByAttributeIndex(nodeIdMapCollection, sectionMember.node.id, 2, [ - Ast.NodeKind.IdentifierPairedExpression, - ]); - if (maybeKeyValuePair === undefined) { - continue; - } - const keyValuePair: TXorNode = maybeKeyValuePair; - - // Add name to scope. - const maybeName: undefined | Ast.TNode = NodeIdMapUtils.maybeAstChildByAttributeIndex( - nodeIdMapCollection, - keyValuePair.node.id, - 0, - [Ast.NodeKind.Identifier], - ); - if (maybeName === undefined) { - continue; - } - const name: Ast.Identifier = maybeName as Ast.Identifier; - - const maybeValue: TXorNode | undefined = NodeIdMapUtils.maybeXorChildByAttributeIndex( - nodeIdMapCollection, - keyValuePair.node.id, - 2, - undefined, - ); - - partial.push({ - source: keyValuePair, - key: name, - keyLiteral: name.literal, - maybeValue, - }); - } - return partial; -} - export function recordKeyValuePairs( nodeIdMapCollection: NodeIdMap.Collection, record: TXorNode, -): ReadonlyArray { +): ReadonlyArray> { const maybeErr: undefined | CommonError.InvariantError = NodeIdMapUtils.testAstAnyNodeKind(record, [ Ast.NodeKind.RecordExpression, Ast.NodeKind.RecordLiteral, @@ -186,7 +123,7 @@ export function recordKeyValuePairs( export function letKeyValuePairs( nodeIdMapCollection: NodeIdMap.Collection, letExpression: TXorNode, -): ReadonlyArray { +): ReadonlyArray> { const maybeErr: undefined | CommonError.InvariantError = NodeIdMapUtils.testAstNodeKind( letExpression, Ast.NodeKind.LetExpression, @@ -204,12 +141,86 @@ export function letKeyValuePairs( return maybeArrayWrapper === undefined ? [] : keyValuePairs(nodeIdMapCollection, maybeArrayWrapper); } -export function keyValuePairs( +export function sectionMemberKeyValuePairs( + nodeIdMapCollection: NodeIdMap.Collection, + section: TXorNode, +): ReadonlyArray> { + const maybeErr: undefined | CommonError.InvariantError = NodeIdMapUtils.testAstNodeKind( + section, + Ast.NodeKind.Section, + ); + if (maybeErr !== undefined) { + throw maybeErr; + } + + if (section.kind === XorNodeKind.Ast) { + return (section.node as Ast.Section).sectionMembers.elements.map((sectionMember: Ast.SectionMember) => { + const namePairedExpression: Ast.IdentifierPairedExpression = sectionMember.namePairedExpression; + return { + source: NodeIdMapUtils.xorNodeFromAst(namePairedExpression), + key: namePairedExpression.key, + keyLiteral: namePairedExpression.key.literal, + maybeValue: NodeIdMapUtils.xorNodeFromAst(namePairedExpression.value), + }; + }); + } + + const maybeSectionMemberArrayWrapper: + | undefined + | TXorNode = NodeIdMapUtils.maybeXorChildByAttributeIndex(nodeIdMapCollection, section.node.id, 4, [ + Ast.NodeKind.ArrayWrapper, + ]); + if (maybeSectionMemberArrayWrapper === undefined) { + return []; + } + const sectionMemberArrayWrapper: TXorNode = maybeSectionMemberArrayWrapper; + + const partial: KeyValuePair[] = []; + for (const sectionMember of expectXorChildren(nodeIdMapCollection, sectionMemberArrayWrapper.node.id)) { + const maybeKeyValuePair: + | undefined + | TXorNode = NodeIdMapUtils.maybeXorChildByAttributeIndex(nodeIdMapCollection, sectionMember.node.id, 2, [ + Ast.NodeKind.IdentifierPairedExpression, + ]); + if (maybeKeyValuePair === undefined) { + continue; + } + const keyValuePair: TXorNode = maybeKeyValuePair; + const keyValuePairNodeId: number = keyValuePair.node.id; + + const maybeKey: undefined | Ast.Identifier = NodeIdMapUtils.maybeAstChildByAttributeIndex( + nodeIdMapCollection, + keyValuePairNodeId, + 0, + [Ast.NodeKind.Identifier], + ) as Ast.Identifier; + if (maybeKey === undefined) { + continue; + } + const key: Ast.Identifier = maybeKey; + + partial.push({ + source: keyValuePair, + key, + keyLiteral: key.literal, + maybeValue: NodeIdMapUtils.maybeXorChildByAttributeIndex( + nodeIdMapCollection, + keyValuePairNodeId, + 2, + undefined, + ), + }); + } + + return partial; +} + +export function keyValuePairs( nodeIdMapCollection: NodeIdMap.Collection, arrayWrapper: TXorNode, -): ReadonlyArray { - const partial: KeyValuePair[] = []; - for (const keyValuePair of arrayWrapperXorNodes(nodeIdMapCollection, arrayWrapper)) { +): ReadonlyArray> { + const partial: KeyValuePair[] = []; + for (const keyValuePair of arrayWrapperCsvXorNodes(nodeIdMapCollection, arrayWrapper)) { const maybeKey: undefined | Ast.TNode = NodeIdMapUtils.maybeAstChildByAttributeIndex( nodeIdMapCollection, keyValuePair.node.id, @@ -219,7 +230,8 @@ export function keyValuePairs( if (maybeKey === undefined) { break; } - const key: Ast.GeneralizedIdentifier | Ast.Identifier = maybeKey as Ast.GeneralizedIdentifier | Ast.Identifier; + const key: T & (Ast.GeneralizedIdentifier | Ast.Identifier) = maybeKey as T & + (Ast.GeneralizedIdentifier | Ast.Identifier); partial.push({ source: keyValuePair, @@ -237,7 +249,7 @@ export function keyValuePairs( return partial; } -export function arrayWrapperXorNodes( +export function arrayWrapperCsvXorNodes( nodeIdMapCollection: NodeIdMap.Collection, arrayWrapper: TXorNode, ): ReadonlyArray { diff --git a/src/settings.ts b/src/settings.ts index 16a09424..6bf4498d 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -17,10 +17,7 @@ export interface ParseSettings extends CommonSettings { readonly newParserState: (parseSettings: ParseSettings, lexerSnapshot: LexerSnapshot) => S & IParserState; } -// tslint:disable-next-line: no-empty-interface -export interface InspectionSettings extends CommonSettings {} - -export type Settings = LexSettings & ParseSettings & InspectionSettings; +export type Settings = LexSettings & ParseSettings; export const DefaultSettings: Settings = { parser: Parser.CombinatorialParser, diff --git a/src/tasks.ts b/src/task.ts similarity index 59% rename from src/tasks.ts rename to src/task.ts index ff9f4c8a..0c1c45e6 100644 --- a/src/tasks.ts +++ b/src/task.ts @@ -3,23 +3,32 @@ import { Inspection } from "."; import { CommonError, Result, ResultUtils } from "./common"; -import { Inspected, TriedInspection } from "./inspection"; +import { ActiveNode, ActiveNodeUtils } from "./inspection/activeNode"; import { Lexer, LexError, LexerSnapshot, TriedLexerSnapshot } from "./lexer"; -import { IParser, IParserState, NodeIdMap, ParseContext, ParseError, ParseOk, TriedParse } from "./parser"; -import { InspectionSettings, LexSettings, ParseSettings, Settings } from "./settings"; +import { IParser, IParserState, NodeIdMap, ParseContext, ParseError, ParseOk, TriedParse, TXorNode } from "./parser"; +import { CommonSettings, LexSettings, ParseSettings } from "./settings"; + +export type TriedInspection = Result; + +export interface InspectionOk { + readonly autocomplete: Inspection.Autocomplete; + readonly maybeInvokeExpression: Inspection.InspectedInvokeExpression; + readonly scope: Inspection.ScopeItemByKey; + readonly scopeType: Inspection.ScopeTypeMap; +} export type TriedLexParse = Result, LexError.TLexError | ParseError.TParseError>; -export type TriedLexParseInspection = Result< - LexParseInspectionOk, - LexError.TLexError | ParseError.TParseError +export type TriedLexParseInspect = Result< + LexParseInspectOk, + CommonError.CommonError | LexError.LexError | ParseError.ParseError >; export interface LexParseOk extends ParseOk { readonly lexerSnapshot: LexerSnapshot; } -export interface LexParseInspectionOk extends Inspected { +export interface LexParseInspectOk extends InspectionOk { readonly triedParse: TriedParse; } @@ -43,7 +52,7 @@ export function tryParse(settings: ParseSettings, lexerSnap } export function tryInspection( - settings: InspectionSettings, + settings: CommonSettings, triedParse: TriedParse, position: Inspection.Position, ): TriedInspection { @@ -71,7 +80,59 @@ export function tryInspection( nodeIdMapCollection = parseOk.nodeIdMapCollection; } - return Inspection.tryFrom(settings, position, nodeIdMapCollection, leafNodeIds, maybeParseError); + const maybeActiveNode: undefined | ActiveNode = ActiveNodeUtils.maybeActiveNode( + position, + nodeIdMapCollection, + leafNodeIds, + ); + if (maybeActiveNode === undefined) { + throw new CommonError.InvariantError(`${tryInspection.name}: couldn't create ActiveNode`); + } + const activeNode: ActiveNode = maybeActiveNode; + const ancestry: ReadonlyArray = maybeActiveNode.ancestry; + + const triedScope: Inspection.TriedScopeForRoot = Inspection.tryScopeForRoot( + settings, + nodeIdMapCollection, + leafNodeIds, + ancestry, + undefined, + ); + if (ResultUtils.isErr(triedScope)) { + return triedScope; + } + const scope: Inspection.ScopeItemByKey = triedScope.value; + + const triedScopeType: Inspection.TriedScopeType = Inspection.tryScopeType(settings, nodeIdMapCollection, scope); + if (ResultUtils.isErr(triedScopeType)) { + return triedScopeType; + } + + const triedAutocomplete: Inspection.TriedAutocomplete = Inspection.tryAutocomplete( + settings, + activeNode, + nodeIdMapCollection, + maybeParseError, + ); + if (ResultUtils.isErr(triedAutocomplete)) { + return triedAutocomplete; + } + + const triedInvokeExpression: Inspection.TriedInvokeExpression = Inspection.tryInvokeExpression( + settings, + nodeIdMapCollection, + activeNode, + ); + if (ResultUtils.isErr(triedInvokeExpression)) { + return triedInvokeExpression; + } + + return ResultUtils.okFactory({ + autocomplete: triedAutocomplete.value, + maybeInvokeExpression: triedInvokeExpression.value, + scope: triedScope.value, + scopeType: triedScopeType.value, + }); } export function tryLexParse( @@ -96,22 +157,19 @@ export function tryLexParse( } export function tryLexParseInspection( - settings: Settings, + settings: LexSettings & ParseSettings, text: string, position: Inspection.Position, -): TriedLexParseInspection { +): TriedLexParseInspect { const triedLexParse: TriedLexParse = tryLexParse(settings, text); if (ResultUtils.isErr(triedLexParse) && triedLexParse.error instanceof LexError.LexError) { return triedLexParse; } // The if statement above should remove LexError from the error type in Result - const casted: Result< - LexParseOk, - ParseError.TParseError | Exclude - > = triedLexParse as Result< + const casted: Result, ParseError.TParseError> = triedLexParse as Result< LexParseOk, - ParseError.TParseError | Exclude + ParseError.TParseError >; const triedInspection: TriedInspection = tryInspection(settings, casted, position); diff --git a/src/test/common.ts b/src/test/common.ts index 00f4392c..d274431d 100644 --- a/src/test/common.ts +++ b/src/test/common.ts @@ -3,12 +3,11 @@ import { expect } from "chai"; import "mocha"; -import { Inspection } from ".."; +import { Inspection, Task } from ".."; import { ResultUtils } from "../common"; import { Lexer, LexerSnapshot, TriedLexerSnapshot } from "../lexer"; import { IParserState, ParseError, ParseOk, TriedParse } from "../parser"; -import { DefaultSettings, LexSettings, ParseSettings } from "../settings"; -import { LexParseOk, TriedLexParse, tryLexParse } from "../tasks"; +import { LexSettings, ParseSettings } from "../settings"; export function expectDeepEqual(partial: X, expected: Y, actualFactoryFn: (partial: X) => Y): void { const actual: Y = actualFactoryFn(partial); @@ -30,58 +29,11 @@ export function expectTextWithPosition(text: string): [string, Inspection.Positi return [text.replace("|", ""), position]; } -export function expectParseOkInspectionOk( - settings: LexSettings & ParseSettings, - text: string, - position: Inspection.Position, -): Inspection.Inspected { - const parseOk: ParseOk = expectParseOk(settings, text); - const triedInspection: Inspection.TriedInspection = Inspection.tryFrom( - DefaultSettings, - position, - parseOk.nodeIdMapCollection, - parseOk.leafNodeIds, - undefined, - ); - return expectInspectionOk(triedInspection); -} - -export function expectParseErrInspectionOk( - settings: LexSettings & ParseSettings, - text: string, - position: Inspection.Position, -): Inspection.Inspected { - const parseError: ParseError.ParseError = expectParseErr(settings, text); - const triedInspection: Inspection.TriedInspection = Inspection.tryFrom( - DefaultSettings, - position, - parseError.state.contextState.nodeIdMapCollection, - parseError.state.contextState.leafNodeIds, - parseError, - ); - return expectInspectionOk(triedInspection); -} - -export function expectParseErrInspectionErr( - settings: LexSettings & ParseSettings, - text: string, - position: Inspection.Position, -): Inspection.TriedInspection { - const parseError: ParseError.ParseError = expectParseErr(settings, text); - return Inspection.tryFrom( - DefaultSettings, - position, - parseError.state.contextState.nodeIdMapCollection, - parseError.state.contextState.leafNodeIds, - parseError, - ); -} - export function expectLexParseOk( settings: LexSettings & ParseSettings, text: string, -): LexParseOk { - const triedLexParse: TriedLexParse = tryLexParse(settings, text); +): Task.LexParseOk { + const triedLexParse: Task.TriedLexParse = Task.tryLexParse(settings, text); if (!ResultUtils.isOk(triedLexParse)) { throw new Error(`AssertFailed: ResultUtils.isOk(triedLexParse): ${triedLexParse.error.message}`); } @@ -136,10 +88,3 @@ function expectTriedParse( const parserState: S & IParserState = settings.newParserState(settings, lexerSnapshot); return settings.parser.readDocument(parserState, settings.parser); } - -export function expectInspectionOk(triedInspection: Inspection.TriedInspection): Inspection.Inspected { - if (!ResultUtils.isOk(triedInspection)) { - throw new Error(`AssertFailed: ResultUtils.isOk(triedInspect) - ${triedInspection.error.message}`); - } - return triedInspection.value; -} diff --git a/src/test/fileUtils.ts b/src/test/fileUtils.ts index 625377ef..a9894594 100644 --- a/src/test/fileUtils.ts +++ b/src/test/fileUtils.ts @@ -1,10 +1,10 @@ import "mocha"; -import { LexSettings, ParseSettings } from "../settings"; +import { Task } from ".."; import { IParserState } from "../parser"; +import { LexSettings, ParseSettings } from "../settings"; import * as fs from "fs"; import * as path from "path"; -import * as Tasks from "../tasks"; const PowerQueryExtensions: ReadonlyArray = [".m", ".mout", ".pq", "pqm"]; @@ -46,9 +46,9 @@ export function writeContents(filePath: string, contents: string): void { export function tryLexParse( settings: LexSettings & ParseSettings, filePath: string, -): Tasks.TriedLexParse { +): Task.TriedLexParse { const contents: string = readContents(filePath); - return Tasks.tryLexParse(settings, contents); + return Task.tryLexParse(settings, contents); } function isDirectory(maybePath: string): boolean { diff --git a/src/test/libraryTest/inspection/autocomplete.ts b/src/test/libraryTest/inspection/autocomplete.ts index d7a759dc..ec33ace3 100644 --- a/src/test/libraryTest/inspection/autocomplete.ts +++ b/src/test/libraryTest/inspection/autocomplete.ts @@ -4,17 +4,60 @@ import { expect } from "chai"; import "mocha"; import { Inspection } from "../../.."; -import { Inspected } from "../../../inspection"; +import { ResultUtils } from "../../../common"; +import { Position, TriedAutocomplete } from "../../../inspection"; +import { ActiveNode, ActiveNodeUtils } from "../../../inspection/activeNode"; import { KeywordKind, TExpressionKeywords } from "../../../lexer"; -import { Ast } from "../../../parser"; -import { DefaultSettings } from "../../../settings"; -import { expectParseErrInspectionOk, expectParseOkInspectionOk, expectTextWithPosition } from "../../common"; +import { Ast, IParserState, NodeIdMap, ParseError, ParseOk } from "../../../parser"; +import { CommonSettings, DefaultSettings, LexSettings, ParseSettings } from "../../../settings"; +import { expectParseErr, expectParseOk, expectTextWithPosition } from "../../common"; + +function expectAutocompleteOk( + settings: CommonSettings, + nodeIdMapCollection: NodeIdMap.Collection, + leafNodeIds: ReadonlyArray, + position: Position, + maybeParseError: ParseError.ParseError | undefined, +): ReadonlyArray { + const maybeActiveNode: undefined | ActiveNode = ActiveNodeUtils.maybeActiveNode( + position, + nodeIdMapCollection, + leafNodeIds, + ); + const triedInspect: TriedAutocomplete = Inspection.tryAutocomplete( + settings, + maybeActiveNode, + nodeIdMapCollection, + maybeParseError, + ); + if (!ResultUtils.isOk(triedInspect)) { + throw new Error(`AssertFailed: ResultUtils.isOk(triedInspect): ${triedInspect.error.message}`); + } + return triedInspect.value; +} -type AbridgedInspection = Inspected["autocompleteKeywords"]; +function expectParseOkAutocompleteOk( + settings: LexSettings & ParseSettings, + text: string, + position: Position, +): ReadonlyArray { + const parseOk: ParseOk = expectParseOk(settings, text); + return expectAutocompleteOk(settings, parseOk.nodeIdMapCollection, parseOk.leafNodeIds, position, undefined); +} -function expectNodesEqual(inspected: Inspection.Inspected, expected: AbridgedInspection): void { - const actual: AbridgedInspection = inspected.autocompleteKeywords; - expect(actual).deep.equal(expected); +function expectParseErrAutocompleteOk( + settings: LexSettings & ParseSettings, + text: string, + position: Position, +): ReadonlyArray { + const parseError: ParseError.ParseError = expectParseErr(settings, text); + return expectAutocompleteOk( + settings, + parseError.state.contextState.nodeIdMapCollection, + parseError.state.contextState.leafNodeIds, + position, + parseError, + ); } describe(`Inspection`, () => { @@ -22,318 +65,318 @@ describe(`Inspection`, () => { describe("partial keyword", () => { it("a|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`a|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("x a|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`x a|`); - const expected: AbridgedInspection = [KeywordKind.And, KeywordKind.As]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.And, KeywordKind.As]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("e|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`e|`); - const expected: AbridgedInspection = [KeywordKind.Each, KeywordKind.Error]; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Each, KeywordKind.Error]; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("if x then x e|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if x then x e|`); - const expected: AbridgedInspection = [KeywordKind.Else]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Else]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("i|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`i|`); - const expected: AbridgedInspection = [KeywordKind.If]; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.If]; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("l|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`l|`); - const expected: AbridgedInspection = [KeywordKind.Let]; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Let]; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("m|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`m|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("x m|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`x m|`); - const expected: AbridgedInspection = [KeywordKind.Meta]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Meta]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("n|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`n|`); - const expected: AbridgedInspection = [KeywordKind.Not]; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Not]; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("true o|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`true o|`); - const expected: AbridgedInspection = [KeywordKind.Or]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Or]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("try true o|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`try true o|`); - const expected: AbridgedInspection = [KeywordKind.Or, KeywordKind.Otherwise]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Or, KeywordKind.Otherwise]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("try true o |", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`try true o |`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("try true ot|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`try true ot|`); - const expected: AbridgedInspection = [KeywordKind.Otherwise]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Otherwise]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("try true oth|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`try true oth|`); - const expected: AbridgedInspection = [KeywordKind.Otherwise]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Otherwise]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("s|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`s|`); - const expected: AbridgedInspection = [KeywordKind.Section]; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Section]; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("[] s|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[] s|`); - const expected: AbridgedInspection = [KeywordKind.Section]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Section]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("section; s|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`section; s|`); - const expected: AbridgedInspection = [KeywordKind.Shared]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Shared]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("section; shared x|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`section; shared x|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("section; [] s|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`section; [] s|`); - const expected: AbridgedInspection = [KeywordKind.Shared]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Shared]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("if true t|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if true t|`); - const expected: AbridgedInspection = [KeywordKind.Then]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Then]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it("t|", () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`t|`); - const expected: AbridgedInspection = [KeywordKind.True, KeywordKind.Try, KeywordKind.Type]; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.True, KeywordKind.Try, KeywordKind.Type]; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); }); describe(`${Ast.NodeKind.ErrorHandlingExpression}`, () => { it(`try |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`try |`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`try true|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`try true|`); - const expected: AbridgedInspection = [KeywordKind.True]; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.True]; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`try true |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`try true |`); - const expected: AbridgedInspection = [KeywordKind.Otherwise]; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Otherwise]; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); }); describe(`${Ast.NodeKind.ErrorRaisingExpression}`, () => { it(`if |error`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if |error`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if error|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if error|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`error |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`error |`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); }); describe(`${Ast.NodeKind.IfExpression}`, () => { it(`if|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if |`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if 1|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if 1|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if |if`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if |if`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if i|f`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if i|f`); - const expected: AbridgedInspection = [KeywordKind.If]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.If]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if 1 |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if 1 |`); - const expected: AbridgedInspection = [KeywordKind.Then]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Then]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if 1 t|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if 1 t|`); - const expected: AbridgedInspection = [KeywordKind.Then]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Then]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if 1 then |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if 1 then |`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if 1 then 1|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if 1 then 1|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if 1 then 1 e|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if 1 then 1 e|`); - const expected: AbridgedInspection = [KeywordKind.Else]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Else]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if 1 then 1 else|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if 1 then 1 else|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if 1 th|en 1 else`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if 1 th|en 1 else`); - const expected: AbridgedInspection = [KeywordKind.Then]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Then]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`if 1 then 1 else |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`if 1 then 1 else |`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); }); describe(`${Ast.NodeKind.InvokeExpression}`, () => { it(`foo(|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(|`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`foo(a|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(a|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`foo(a|,`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(a|,`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`foo(a,|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(a,|`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); }); describe(`${Ast.NodeKind.ListExpression}`, () => { it(`{|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`{|`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`{1|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`{1|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`{1|,`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`{1|,`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`{1,|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`{1,|`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`{1,|2`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`{1,|2`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`{1,|2,`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`{1,|2,`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`{1..|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`{1..|`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); }); @@ -342,136 +385,136 @@ describe(`Inspection`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition( `try true otherwise| false`, ); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`try true otherwise |false`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition( `try true otherwise |false`, ); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`try true oth|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`try true oth|`); - const expected: AbridgedInspection = [KeywordKind.Otherwise]; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = [KeywordKind.Otherwise]; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`try true otherwise |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`try true otherwise |`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); }); describe(`${Ast.NodeKind.ParenthesizedExpression}`, () => { it(`+(|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+(|`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); }); describe(`${Ast.NodeKind.RecordExpression}`, () => { it(`+[|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=|`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=1|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=1|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a|=1`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a|=1`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=1|]`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=1|]`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=| 1]`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=| 1]`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseOkAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=1|,`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=1|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=1,|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=1,|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=1|,b`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=1|,b`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=1|,b=`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=1|,b=`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=|1,b=`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=|1,b=`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=1,b=2|`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=1,b=2|`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`+[a=1,b=2 |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`+[a=1,b=2 |`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); }); describe(`${Ast.NodeKind.SectionMember}`, () => { it(`section; [] |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`section; [] |`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`section; [] x |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`section; [] x |`); - const expected: AbridgedInspection = []; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = []; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); it(`section; x = |`, () => { const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`section; x = |`); - const expected: AbridgedInspection = TExpressionKeywords; - expectNodesEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected); + const expected: ReadonlyArray = TExpressionKeywords; + expect(expectParseErrAutocompleteOk(DefaultSettings, text, position)).deep.equal(expected); }); }); }); diff --git a/src/test/libraryTest/inspection/invokeExpression.ts b/src/test/libraryTest/inspection/invokeExpression.ts new file mode 100644 index 00000000..fc4ea2ab --- /dev/null +++ b/src/test/libraryTest/inspection/invokeExpression.ts @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { expect } from "chai"; +import "mocha"; +import { Inspection } from "../../.."; +import { ResultUtils } from "../../../common"; +import { InspectedInvokeExpression, Position } from "../../../inspection"; +import { ActiveNode, ActiveNodeUtils } from "../../../inspection/activeNode"; +import { IParserState, NodeIdMap, ParseError, ParseOk } from "../../../parser"; +import { CommonSettings, DefaultSettings, LexSettings, ParseSettings } from "../../../settings"; +import { expectParseErr, expectParseOk, expectTextWithPosition } from "../../common"; + +function expectInvokeExpressionOk( + settings: CommonSettings, + nodeIdMapCollection: NodeIdMap.Collection, + leafNodeIds: ReadonlyArray, + position: Position, +): undefined | InspectedInvokeExpression { + const maybeActiveNode: undefined | ActiveNode = ActiveNodeUtils.maybeActiveNode( + position, + nodeIdMapCollection, + leafNodeIds, + ); + if (!(maybeActiveNode !== undefined)) { + throw new Error(`AssertedFailed: maybeActiveNode !== undefined`); + } + const activeNode: ActiveNode = maybeActiveNode; + + const triedInspect: Inspection.TriedInvokeExpression = Inspection.tryInvokeExpression( + settings, + nodeIdMapCollection, + activeNode, + ); + if (!ResultUtils.isOk(triedInspect)) { + throw new Error(`AssertFailed: ResultUtils.isOk(triedInspect): ${triedInspect.error.message}`); + } + return triedInspect.value; +} + +function expectParseOkInvokeExpressionOk( + settings: LexSettings & ParseSettings, + text: string, + position: Position, +): undefined | InspectedInvokeExpression { + const parseOk: ParseOk = expectParseOk(settings, text); + return expectInvokeExpressionOk(settings, parseOk.nodeIdMapCollection, parseOk.leafNodeIds, position); +} + +function expectParseErrInvokeExpressionOk( + settings: LexSettings & ParseSettings, + text: string, + position: Position, +): undefined | InspectedInvokeExpression { + const parseError: ParseError.ParseError = expectParseErr(settings, text); + return expectInvokeExpressionOk( + settings, + parseError.state.contextState.nodeIdMapCollection, + parseError.state.contextState.leafNodeIds, + position, + ); +} + +describe(`subset Inspection - InvokeExpression`, () => { + it("single invoke expression, no parameters", () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition("Foo(|)"); + const inspected: undefined | InspectedInvokeExpression = expectParseOkInvokeExpressionOk( + DefaultSettings, + text, + position, + ); + if (!(inspected !== undefined)) { + throw new Error(`AssertFailed: inspected !== undefined`); + } + + expect(inspected.maybeName).to.equal("Foo"); + expect(inspected.maybeArguments).to.equal(undefined, "expected no arguments"); + }); + + it("multiple invoke expression, no parameters", () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition("Bar(Foo(|))"); + const inspected: undefined | InspectedInvokeExpression = expectParseOkInvokeExpressionOk( + DefaultSettings, + text, + position, + ); + if (!(inspected !== undefined)) { + throw new Error(`AssertFailed: inspected !== undefined`); + } + + expect(inspected.maybeName).to.equal("Foo"); + expect(inspected.maybeArguments).to.equal(undefined, "expected no arguments"); + }); + + it("single invoke expression - Foo(a|)", () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition("Foo(a|)"); + const inspected: undefined | InspectedInvokeExpression = expectParseOkInvokeExpressionOk( + DefaultSettings, + text, + position, + ); + if (!(inspected !== undefined)) { + throw new Error(`AssertFailed: inspected !== undefined`); + } + + expect(inspected.maybeName).to.equal("Foo"); + expect(inspected.maybeArguments).not.equal(undefined, "expected arguments"); + expect(inspected.maybeArguments?.numArguments).equal(1); + expect(inspected.maybeArguments?.positionArgumentIndex).equal(0); + }); + + it("single invoke expression - Foo(a|,)", () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition("Foo(a|,)"); + const inspected: undefined | InspectedInvokeExpression = expectParseErrInvokeExpressionOk( + DefaultSettings, + text, + position, + ); + if (!(inspected !== undefined)) { + throw new Error(`AssertFailed: inspected !== undefined`); + } + + expect(inspected.maybeName).to.equal("Foo"); + expect(inspected.maybeArguments).not.equal(undefined, "expected arguments"); + expect(inspected.maybeArguments?.numArguments).equal(2); + expect(inspected.maybeArguments?.positionArgumentIndex).equal(0); + }); + + it("single invoke expression - Foo(a,|)", () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition("Foo(a,|)"); + const inspected: undefined | InspectedInvokeExpression = expectParseErrInvokeExpressionOk( + DefaultSettings, + text, + position, + ); + if (!(inspected !== undefined)) { + throw new Error(`AssertFailed: inspected !== undefined`); + } + + expect(inspected.maybeName).to.equal("Foo"); + expect(inspected.maybeArguments).not.equal(undefined, "expected arguments"); + expect(inspected.maybeArguments?.numArguments).equal(2); + expect(inspected.maybeArguments?.positionArgumentIndex).equal(1); + }); +}); diff --git a/src/test/libraryTest/inspection/scope.ts b/src/test/libraryTest/inspection/scope.ts new file mode 100644 index 00000000..b5567dd0 --- /dev/null +++ b/src/test/libraryTest/inspection/scope.ts @@ -0,0 +1,904 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "mocha"; +import { Inspection } from "../../.."; +import { isNever, ResultUtils } from "../../../common"; +import { Position, ScopeItemByKey, ScopeItemKind } from "../../../inspection"; +import { ActiveNode, ActiveNodeUtils } from "../../../inspection/activeNode"; +import { Ast, IParserState, NodeIdMap, ParseError, ParseOk } from "../../../parser"; +import { CommonSettings, DefaultSettings, LexSettings, ParseSettings } from "../../../settings"; +import { expectDeepEqual, expectParseErr, expectParseOk, expectTextWithPosition } from "../../common"; + +export type TAbridgedNodeScopeItem = + | AbridgedEachScopeItem + | AbridgedKeyValuePairScopeItem + | AbridgedParameterScopeItem + | AbridgedSectionMemberScopeItem + | AbridgedUndefinedScopeItem; + +type AbridgedNodeScope = ReadonlyArray; + +interface IAbridgedNodeScopeItem { + readonly identifier: string; + readonly kind: ScopeItemKind; +} + +interface AbridgedEachScopeItem extends IAbridgedNodeScopeItem { + readonly kind: ScopeItemKind.Each; + readonly eachExpressionNodeId: number; +} + +interface AbridgedKeyValuePairScopeItem extends IAbridgedNodeScopeItem { + readonly kind: ScopeItemKind.KeyValuePair; + readonly keyNodeId: number; + readonly maybeValueNodeId: undefined | number; +} + +interface AbridgedParameterScopeItem extends IAbridgedNodeScopeItem { + readonly kind: ScopeItemKind.Parameter; + readonly nameNodeId: number; + readonly isNullable: boolean; + readonly isOptional: boolean; + readonly maybeType: Ast.PrimitiveTypeConstantKind | undefined; +} + +interface AbridgedSectionMemberScopeItem extends IAbridgedNodeScopeItem { + readonly kind: ScopeItemKind.SectionMember; + readonly keyNodeId: number; +} + +interface AbridgedUndefinedScopeItem extends IAbridgedNodeScopeItem { + readonly kind: ScopeItemKind.Undefined; + readonly nodeId: number; +} + +function abridgedScopeItemFrom(identifier: string, scopeItem: Inspection.TScopeItem2): TAbridgedNodeScopeItem { + switch (scopeItem.kind) { + case ScopeItemKind.Each: + return { + identifier, + kind: scopeItem.kind, + eachExpressionNodeId: scopeItem.eachExpression.node.id, + }; + + case ScopeItemKind.KeyValuePair: + return { + identifier, + kind: scopeItem.kind, + keyNodeId: scopeItem.key.id, + maybeValueNodeId: scopeItem.maybeValue !== undefined ? scopeItem.maybeValue.node.id : undefined, + }; + + case ScopeItemKind.Parameter: + return { + identifier, + kind: scopeItem.kind, + nameNodeId: scopeItem.name.id, + isNullable: scopeItem.isNullable, + isOptional: scopeItem.isOptional, + maybeType: scopeItem.maybeType, + }; + + case ScopeItemKind.SectionMember: + return { + identifier, + kind: scopeItem.kind, + keyNodeId: scopeItem.key.id, + }; + + case ScopeItemKind.Undefined: + return { + identifier, + kind: scopeItem.kind, + nodeId: scopeItem.xorNode.node.id, + }; + + default: + throw isNever(scopeItem); + } +} + +function actualScopeFactoryFn(scopeItemByKey: ScopeItemByKey): ReadonlyArray { + const result: TAbridgedNodeScopeItem[] = []; + + for (const [identifier, scopeItem] of scopeItemByKey.entries()) { + result.push(abridgedScopeItemFrom(identifier, scopeItem)); + } + + return result; +} + +function actualParameterFactoryFn(scopeItemByKey: ScopeItemByKey): ReadonlyArray { + const result: AbridgedParameterScopeItem[] = []; + + for (const [identifier, scopeItem] of scopeItemByKey.entries()) { + const abridged: TAbridgedNodeScopeItem = abridgedScopeItemFrom(identifier, scopeItem); + if (abridged.kind === ScopeItemKind.Parameter) { + result.push(abridged); + } + } + + return result; +} + +function expectScopeForNodeOk( + settings: CommonSettings, + nodeIdMapCollection: NodeIdMap.Collection, + leafNodeIds: ReadonlyArray, + position: Position, +): ScopeItemByKey { + const maybeActiveNode: undefined | ActiveNode = ActiveNodeUtils.maybeActiveNode( + position, + nodeIdMapCollection, + leafNodeIds, + ); + if (maybeActiveNode === undefined) { + return new Map(); + } + const activeNode: ActiveNode = maybeActiveNode; + + const triedScopeInspection: Inspection.TriedScopeForRoot = Inspection.tryScopeForRoot( + settings, + nodeIdMapCollection, + leafNodeIds, + activeNode.ancestry, + undefined, + ); + if (!ResultUtils.isOk(triedScopeInspection)) { + throw new Error(`AssertFailed: ResultUtils.isOk(triedScopeInspection): ${triedScopeInspection.error.message}`); + } + return triedScopeInspection.value; +} + +export function expectParseOkScopeOk( + settings: LexSettings & ParseSettings, + text: string, + position: Position, +): ScopeItemByKey { + const parseOk: ParseOk = expectParseOk(settings, text); + return expectScopeForNodeOk(settings, parseOk.nodeIdMapCollection, parseOk.leafNodeIds, position); +} + +export function expectParseErrScopeOk( + settings: LexSettings & ParseSettings, + text: string, + position: Position, +): ScopeItemByKey { + const parseError: ParseError.ParseError = expectParseErr(settings, text); + return expectScopeForNodeOk( + settings, + parseError.state.contextState.nodeIdMapCollection, + parseError.state.contextState.leafNodeIds, + position, + ); +} + +describe(`subset Inspection - Scope - Identifier`, () => { + describe(`Scope`, () => { + describe(`${Ast.NodeKind.EachExpression} (Ast)`, () => { + it(`|each 1`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|each 1`); + const expected: ReadonlyArray = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`each| 1`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each| 1`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`each |1`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each |1`); + const expected: AbridgedNodeScope = [ + { + identifier: "_", + kind: ScopeItemKind.Each, + eachExpressionNodeId: 1, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`each 1|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each 1|`); + const expected: AbridgedNodeScope = [ + { + identifier: "_", + kind: ScopeItemKind.Each, + eachExpressionNodeId: 1, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`each each 1|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each each 1|`); + const expected: AbridgedNodeScope = [ + { + identifier: "_", + kind: ScopeItemKind.Each, + eachExpressionNodeId: 3, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + + describe(`${Ast.NodeKind.EachExpression} (ParserContext)`, () => { + it(`each|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each|`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`each |`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each |`); + const expected: AbridgedNodeScope = [ + { + identifier: "_", + kind: ScopeItemKind.Each, + eachExpressionNodeId: 1, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + + describe(`${Ast.NodeKind.FunctionExpression} (Ast)`, () => { + it(`|(x) => z`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|(x) => z`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`(x|, y) => z`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x|, y) => z`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`(x, y)| => z`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x, y)| => z`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`(x, y) => z|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x, y) => z|`); + const expected: AbridgedNodeScope = [ + { + identifier: "x", + kind: ScopeItemKind.Parameter, + nameNodeId: 7, + isNullable: true, + isOptional: false, + maybeType: undefined, + }, + { + identifier: "y", + kind: ScopeItemKind.Parameter, + nameNodeId: 11, + isNullable: true, + isOptional: false, + maybeType: undefined, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + + describe(`${Ast.NodeKind.FunctionExpression} (ParserContext)`, () => { + it(`|(x) =>`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|(x) =>`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`(x|, y) =>`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x|, y) =>`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`(x, y)| =>`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x, y)| =>`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`(x, y) =>|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x, y) =>|`); + const expected: AbridgedNodeScope = [ + { + identifier: "x", + kind: ScopeItemKind.Parameter, + nameNodeId: 7, + isNullable: true, + isOptional: false, + maybeType: undefined, + }, + { + identifier: "y", + kind: ScopeItemKind.Parameter, + nameNodeId: 11, + isNullable: true, + isOptional: false, + maybeType: undefined, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + + describe(`${Ast.NodeKind.IdentifierExpression} (Ast)`, () => { + it(`let x = 1, y = x in 1|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `let x = 1, y = x in 1|`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "x", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 6, + maybeValueNodeId: 9, + }, + { + identifier: "y", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 13, + maybeValueNodeId: 16, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + + describe(`${Ast.NodeKind.RecordExpression} (Ast)`, () => { + it(`|[a=1]`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|[a=1]`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[|a=1]`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[|a=1]`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=1|]`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1|]`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=1, b=2|]`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1, b=2|]`); + const expected: AbridgedNodeScope = [ + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 7, + maybeValueNodeId: 10, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=1, b=2|, c=3]`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1, b=2|, c=3]`); + const expected: AbridgedNodeScope = [ + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 7, + maybeValueNodeId: 10, + }, + { + identifier: "c", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 21, + maybeValueNodeId: 24, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=1]|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1]|`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=[|b=1]]`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=[|b=1]]`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + + describe(`${Ast.NodeKind.RecordExpression} (ParserContext)`, () => { + it(`|[a=1`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|[a=1`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[|a=1`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[|a=1`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=|1`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=|1`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=1|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1|`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=1, b=|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1, b=|`); + const expected: AbridgedNodeScope = [ + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 7, + maybeValueNodeId: 9, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=1, b=2|, c=3`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1, b=2|, c=3`); + const expected: AbridgedNodeScope = [ + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 7, + maybeValueNodeId: 9, + }, + { + identifier: "c", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 19, + maybeValueNodeId: 21, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=[|b=1`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=[|b=1`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`[a=[b=|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=[b=|`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + + describe(`${Ast.NodeKind.Section} (Ast)`, () => { + it(`s|ection foo; x = 1; y = 2;`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `s|ection foo; x = 1; y = 2;`, + ); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`section foo; x = 1|; y = 2;`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `section foo; x = 1|; y = 2;`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "y", + kind: ScopeItemKind.SectionMember, + keyNodeId: 15, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`section foo; x = 1; y = 2|;`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `section foo; x = 1; y = 2|;`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "x", + kind: ScopeItemKind.SectionMember, + keyNodeId: 8, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`section foo; x = 1; y = 2;|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `section foo; x = 1; y = 2;|`, + ); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`section foo; x = 1; y = 2; z = let a = 1 in |b;`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `section foo; x = 1; y = 2; z = let a = 1 in |b;`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "x", + kind: ScopeItemKind.SectionMember, + keyNodeId: 8, + }, + { + identifier: "y", + kind: ScopeItemKind.SectionMember, + keyNodeId: 15, + }, + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 29, + maybeValueNodeId: 32, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + + describe(`${Ast.NodeKind.SectionMember} (ParserContext)`, () => { + it(`s|ection foo; x = 1; y = 2`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `s|ection foo; x = 1; y = 2`, + ); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`section foo; x = 1|; y = 2`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `section foo; x = 1|; y = 2`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "y", + kind: ScopeItemKind.SectionMember, + keyNodeId: 15, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`section foo; x = 1; y = 2|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `section foo; x = 1; y = 2|`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "x", + kind: ScopeItemKind.SectionMember, + keyNodeId: 8, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`section foo; x = 1; y = () => 10|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `section foo; x = 1; y = () => 10|`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "x", + kind: ScopeItemKind.SectionMember, + keyNodeId: 8, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + + describe(`${Ast.NodeKind.LetExpression} (Ast)`, () => { + it(`let a = 1 in |x`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1 in |x`); + const expected: AbridgedNodeScope = [ + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 6, + maybeValueNodeId: 9, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`let a = 1 in x|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1 in x|`); + const expected: AbridgedNodeScope = [ + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 6, + maybeValueNodeId: 9, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`let a = |1 in x`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = |1 in x`); + const expected: AbridgedNodeScope = []; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`let a = 1, b = 2 in x|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `let a = 1, b = 2 in x|`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 6, + maybeValueNodeId: 9, + }, + { + identifier: "b", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 13, + maybeValueNodeId: 16, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`let a = 1|, b = 2 in x`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `let a = 1|, b = 2 in x`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "b", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 13, + maybeValueNodeId: 16, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`(p1, p2) => let a = 1, b = 2, c = 3| in c`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `(p1, p2) => let a = 1, b = 2, c = 3| in c`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "p1", + kind: ScopeItemKind.Parameter, + nameNodeId: 7, + isNullable: true, + isOptional: false, + maybeType: undefined, + }, + { + identifier: "p2", + kind: ScopeItemKind.Parameter, + nameNodeId: 11, + isNullable: true, + isOptional: false, + maybeType: undefined, + }, + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 19, + maybeValueNodeId: 22, + }, + { + identifier: "b", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 26, + maybeValueNodeId: 29, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`let eggs = let ham = 0 in 1, foo = 2, bar = 3 in 4|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `let eggs = let ham = 0 in 1, foo = 2, bar = 3 in 4|`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "eggs", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 6, + maybeValueNodeId: 8, + }, + { + identifier: "foo", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 23, + maybeValueNodeId: 26, + }, + { + identifier: "bar", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 30, + maybeValueNodeId: 33, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`let eggs = let ham = 0 in |1, foo = 2, bar = 3 in 4`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `let eggs = let ham = 0 in |1, foo = 2, bar = 3 in 4`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "foo", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 23, + maybeValueNodeId: 26, + }, + { + identifier: "bar", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 30, + maybeValueNodeId: 33, + }, + { + identifier: "ham", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 13, + maybeValueNodeId: 16, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + + describe(`${Ast.NodeKind.LetExpression} (ParserContext)`, () => { + it(`let a = 1 in |`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1 in |`); + const expected: AbridgedNodeScope = [ + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 6, + maybeValueNodeId: 9, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`let a = 1, b = 2 in |`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1, b = 2 in |`); + const expected: AbridgedNodeScope = [ + { + identifier: "a", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 6, + maybeValueNodeId: 9, + }, + { + identifier: "b", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 13, + maybeValueNodeId: 16, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`let a = 1|, b = 2 in`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1|, b = 2 in `); + const expected: AbridgedNodeScope = [ + { + identifier: "b", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 13, + maybeValueNodeId: 16, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`let x = (let y = 1 in z|) in`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `let x = (let y = 1 in z|) in`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "y", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 16, + maybeValueNodeId: 19, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + + it(`let x = (let y = 1 in z) in |`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `let x = (let y = 1 in z) in |`, + ); + const expected: AbridgedNodeScope = [ + { + identifier: "x", + kind: ScopeItemKind.KeyValuePair, + keyNodeId: 6, + maybeValueNodeId: 9, + }, + ]; + expectDeepEqual(expectParseErrScopeOk(DefaultSettings, text, position), expected, actualScopeFactoryFn); + }); + }); + }); + + describe(`Parameter`, () => { + it(`(a, b as number, c as nullable function, optional d, optional e as table) => 1|`, () => { + const [text, position]: [string, Inspection.Position] = expectTextWithPosition( + `(a, b as number, c as nullable function, optional d, optional e as table) => 1|`, + ); + const expected: ReadonlyArray = [ + { + identifier: "a", + kind: ScopeItemKind.Parameter, + nameNodeId: 7, + isNullable: true, + isOptional: false, + maybeType: undefined, + }, + { + identifier: "b", + kind: ScopeItemKind.Parameter, + nameNodeId: 11, + isNullable: false, + isOptional: false, + maybeType: Ast.PrimitiveTypeConstantKind.Number, + }, + { + identifier: "c", + kind: ScopeItemKind.Parameter, + nameNodeId: 19, + isNullable: true, + isOptional: false, + maybeType: Ast.PrimitiveTypeConstantKind.Function, + }, + { + identifier: "d", + kind: ScopeItemKind.Parameter, + nameNodeId: 30, + isNullable: true, + isOptional: true, + maybeType: undefined, + }, + { + identifier: "e", + kind: ScopeItemKind.Parameter, + nameNodeId: 35, + isNullable: false, + isOptional: true, + maybeType: Ast.PrimitiveTypeConstantKind.Table, + }, + ]; + expectDeepEqual(expectParseOkScopeOk(DefaultSettings, text, position), expected, actualParameterFactoryFn); + }); + }); +}); diff --git a/src/test/libraryTest/inspection/scope/ancestry.ts b/src/test/libraryTest/inspection/scope/ancestry.ts deleted file mode 100644 index d0774b4c..00000000 --- a/src/test/libraryTest/inspection/scope/ancestry.ts +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import "mocha"; -import { Inspection } from "../../../.."; -import { isNever } from "../../../../common"; -import { Token } from "../../../../lexer"; -import { Ast, TXorNode, XorNodeKind } from "../../../../parser"; -import { DefaultSettings } from "../../../../settings"; -import { - expectDeepEqual, - expectParseErrInspectionOk, - expectParseOkInspectionOk, - expectTextWithPosition, -} from "../../../common"; - -interface AbridgedTravelPathNode { - readonly id: number; - readonly kind: Ast.NodeKind; - readonly maybePositionStartCodeUnit: number | undefined; -} - -function actualFactoryFn(inspected: Inspection.Inspected): ReadonlyArray { - if (inspected.maybeActiveNode === undefined) { - return []; - } - - return inspected.maybeActiveNode.ancestry.map((xorNode: TXorNode) => { - let maybePositionStartCodeUnit: number | undefined; - - switch (xorNode.kind) { - case XorNodeKind.Ast: - maybePositionStartCodeUnit = xorNode.node.tokenRange.positionStart.codeUnit; - break; - - case XorNodeKind.Context: - const maybeTokenStart: Token | undefined = xorNode.node.maybeTokenStart; - maybePositionStartCodeUnit = - maybeTokenStart !== undefined ? maybeTokenStart.positionStart.codeUnit : undefined; - break; - - default: - throw isNever(xorNode); - } - - return { - id: xorNode.node.id, - kind: xorNode.node.kind, - maybePositionStartCodeUnit, - }; - }); -} - -describe(`Inspection - Scope - Ancestry`, () => { - describe(`${Ast.NodeKind.RecordExpression} (Ast)`, () => { - it(`|[foo = bar]`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|[foo = bar]`); - const expected: ReadonlyArray = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[foo| = bar]`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[foo| = bar]`); - const expected: ReadonlyArray = [ - { - id: 7, - kind: Ast.NodeKind.GeneralizedIdentifier, - maybePositionStartCodeUnit: 1, - }, - { - id: 6, - kind: Ast.NodeKind.GeneralizedIdentifierPairedExpression, - maybePositionStartCodeUnit: 1, - }, - { - id: 5, - kind: Ast.NodeKind.Csv, - maybePositionStartCodeUnit: 1, - }, - { - id: 4, - kind: Ast.NodeKind.ArrayWrapper, - maybePositionStartCodeUnit: 1, - }, - { - id: 2, - kind: Ast.NodeKind.RecordExpression, - maybePositionStartCodeUnit: 0, - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[foo = bar|]`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[foo = bar|]`); - const expected: ReadonlyArray = [ - { - id: 11, - kind: Ast.NodeKind.Identifier, - maybePositionStartCodeUnit: 7, - }, - { - id: 10, - kind: Ast.NodeKind.IdentifierExpression, - maybePositionStartCodeUnit: 7, - }, - { - id: 6, - kind: Ast.NodeKind.GeneralizedIdentifierPairedExpression, - maybePositionStartCodeUnit: 1, - }, - { - id: 5, - kind: Ast.NodeKind.Csv, - maybePositionStartCodeUnit: 1, - }, - { - id: 4, - kind: Ast.NodeKind.ArrayWrapper, - maybePositionStartCodeUnit: 1, - }, - { - id: 2, - kind: Ast.NodeKind.RecordExpression, - maybePositionStartCodeUnit: 0, - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.RecordExpression} (ParserContext)`, () => { - it(`|[foo = bar`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|[foo = bar`); - const expected: ReadonlyArray = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[foo| = bar`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[foo| = bar`); - const expected: ReadonlyArray = [ - { - id: 7, - kind: Ast.NodeKind.GeneralizedIdentifier, - maybePositionStartCodeUnit: 1, - }, - { - id: 6, - kind: Ast.NodeKind.GeneralizedIdentifierPairedExpression, - maybePositionStartCodeUnit: 1, - }, - { - id: 5, - kind: Ast.NodeKind.Csv, - maybePositionStartCodeUnit: 1, - }, - { - id: 4, - kind: Ast.NodeKind.ArrayWrapper, - maybePositionStartCodeUnit: 1, - }, - { - id: 2, - kind: Ast.NodeKind.RecordExpression, - maybePositionStartCodeUnit: 0, - }, - { - id: 1, - kind: Ast.NodeKind.LogicalExpression, - maybePositionStartCodeUnit: 0, - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[foo = bar|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[foo = bar|`); - const expected: ReadonlyArray = [ - { - id: 11, - kind: Ast.NodeKind.Identifier, - maybePositionStartCodeUnit: 7, - }, - { - id: 10, - kind: Ast.NodeKind.IdentifierExpression, - maybePositionStartCodeUnit: 7, - }, - { - id: 6, - kind: Ast.NodeKind.GeneralizedIdentifierPairedExpression, - maybePositionStartCodeUnit: 1, - }, - { - id: 5, - kind: Ast.NodeKind.Csv, - maybePositionStartCodeUnit: 1, - }, - { - id: 4, - kind: Ast.NodeKind.ArrayWrapper, - maybePositionStartCodeUnit: 1, - }, - { - id: 2, - kind: Ast.NodeKind.RecordExpression, - maybePositionStartCodeUnit: 0, - }, - { - id: 1, - kind: Ast.NodeKind.LogicalExpression, - maybePositionStartCodeUnit: 0, - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); -}); diff --git a/src/test/libraryTest/inspection/scope/identifier.ts b/src/test/libraryTest/inspection/scope/identifier.ts deleted file mode 100644 index 154021ed..00000000 --- a/src/test/libraryTest/inspection/scope/identifier.ts +++ /dev/null @@ -1,818 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import "mocha"; -import { Inspection } from "../../../.."; -import { ScopeItemKind } from "../../../../inspection"; -import { Ast } from "../../../../parser"; -import { DefaultSettings } from "../../../../settings"; -import { - expectDeepEqual, - expectParseErrInspectionOk, - expectParseOkInspectionOk, - expectTextWithPosition, -} from "../../../common"; - -type AbridgedScope = ReadonlyArray; - -interface AbridgedScopeItem { - readonly key: string; - readonly kind: ScopeItemKind; -} - -function actualFactoryFn(inspected: Inspection.Inspected): AbridgedScope { - const abridgedScopeItems: AbridgedScopeItem[] = []; - - for (const [key, scopeItem] of inspected.scope.entries()) { - abridgedScopeItems.push({ - key, - kind: scopeItem.kind, - }); - } - - return abridgedScopeItems; -} - -describe(`Inspection - Scope - Identifier`, () => { - describe(`${Ast.NodeKind.EachExpression} (Ast)`, () => { - it(`|each 1`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|each 1`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`each| 1`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each| 1`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`each |1`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each |1`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Each, - key: "_", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`each 1|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each 1|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Each, - key: "_", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`each each 1|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each each 1|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Each, - key: "_", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.EachExpression} (ParserContext)`, () => { - it(`each|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each|`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`each |`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`each |`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Each, - key: "_", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.FunctionExpression} (Ast)`, () => { - it(`|(x) => z`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|(x) => z`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`(x|, y) => z`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x|, y) => z`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`(x, y)| => z`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x, y)| => z`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`(x, y) => z|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x, y) => z|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "z", - }, - { - kind: ScopeItemKind.Parameter, - key: "x", - }, - { - kind: ScopeItemKind.Parameter, - key: "y", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.FunctionExpression} (ParserContext)`, () => { - it(`|(x) =>`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|(x) =>`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`(x|, y) =>`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x|, y) =>`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`(x, y)| =>`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x, y)| =>`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`(x, y) =>|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`(x, y) =>|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Parameter, - key: "x", - }, - { - kind: ScopeItemKind.Parameter, - key: "y", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.IdentifierExpression} (Ast)`, () => { - it(`|foo`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|foo`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`f|oo`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`f|oo`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "foo", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`foo|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "foo", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`|@foo`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|@foo`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`@|foo`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`@|foo`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "@foo", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`@f|oo`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`@f|oo`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "@foo", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`@foo|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`@foo|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "@foo", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.InvokeExpression} (Ast)`, () => { - it(`|foo(x)`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|foo(x)`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`foo(x, y|)`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(x, y|)`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "y", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`foo(x, y)|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(x, y)|`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[x](y|)`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[x](y|)`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "y", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`foo(|)`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(|)`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.InvokeExpression} (ParserContext)`, () => { - it(`|foo(x`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|foo(x`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`foo(x, y|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(x, y|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "y", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`foo(|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(|`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`foo(x,|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(x,|`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`foo(x, |`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`foo(x, |`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[x](y|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[x](y|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "y", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.ListExpression} (ParserContext)`, () => { - it(`|{1`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|{1`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`{|1`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`{|1`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`{1|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`{1|`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`{{|1`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`{{|1`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.RecordExpression} (Ast)`, () => { - it(`|[a=1]`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|[a=1]`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[|a=1]`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[|a=1]`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=1|]`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1|]`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=1, b=2|]`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1, b=2|]`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=1, b=2|, c=3]`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1, b=2|, c=3]`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "c", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=1]|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1]|`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=[|b=1]]`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=[|b=1]]`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.RecordExpression} (ParserContext)`, () => { - it(`|[a=1`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`|[a=1`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[|a=1`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[|a=1`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=|1`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=|1`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=1|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1|`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=1, b=|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1, b=|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=1, b=2|, c=3`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=1, b=2|, c=3`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "c", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=[|b=1`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=[|b=1`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`[a=[b=|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`[a=[b=|`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.SectionMember} (Ast)`, () => { - it(`s|ection foo; x = 1; y = 2;`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `s|ection foo; x = 1; y = 2;`, - ); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`section foo; x = 1|; y = 2;`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `section foo; x = 1|; y = 2;`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.SectionMember, - key: "y", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`section foo; x = 1; y = 2|;`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `section foo; x = 1; y = 2|;`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.SectionMember, - key: "x", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`section foo; x = 1; y = 2;|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `section foo; x = 1; y = 2;|`, - ); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`section foo; x = 1; y = 2; z = let a = 1 in |b;`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `section foo; x = 1; y = 2; z = let a = 1 in |b;`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - { - kind: ScopeItemKind.SectionMember, - key: "x", - }, - { - kind: ScopeItemKind.SectionMember, - key: "y", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.SectionMember} (ParserContext)`, () => { - it(`s|ection foo; x = 1; y = 2`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `s|ection foo; x = 1; y = 2`, - ); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`section foo; x = 1|; y = 2`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `section foo; x = 1|; y = 2`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.SectionMember, - key: "y", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`section foo; x = 1; y = 2|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `section foo; x = 1; y = 2|`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.SectionMember, - key: "x", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`section foo; x = 1; y = () => 10|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `section foo; x = 1; y = () => 10|`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.SectionMember, - key: "x", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.LetExpression} (Ast)`, () => { - it(`let a = 1 in |x`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1 in |x`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let a = 1 in x|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1 in x|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "x", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let a = |1 in x`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = |1 in x`); - const expected: AbridgedScope = []; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let a = 1, b = 2 in x|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1, b = 2 in x|`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "x", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "b", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let a = 1|, b = 2 in x`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1|, b = 2 in x`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "b", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`(p1, p2) => let a = 1, b = 2, c = 3| in c`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `(p1, p2) => let a = 1, b = 2, c = 3| in c`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "b", - }, - { - kind: ScopeItemKind.Parameter, - key: "p1", - }, - { - kind: ScopeItemKind.Parameter, - key: "p2", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let eggs = let ham = 0 in 1, foo = 2, bar = 3 in 4|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `let eggs = let ham = 0 in 1, foo = 2, bar = 3 in 4|`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "eggs", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "foo", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "bar", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let eggs = let ham = 0 in |1, foo = 2, bar = 3 in 4`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `let eggs = let ham = 0 in |1, foo = 2, bar = 3 in 4`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "ham", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "foo", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "bar", - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); - - describe(`${Ast.NodeKind.LetExpression} (ParserContext)`, () => { - it(`let a = 1 in |`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1 in |`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let a = 1, b = 2 in |`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1, b = 2 in |`); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "b", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let a = 1|, b = 2 in`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition(`let a = 1|, b = 2 in `); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "b", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let a = 1, b = 2, c = 3 in |`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `let a = 1, b = 2, c = 3 in |`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "a", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "b", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "c", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let x = (let y = 1 in z|) in`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `let x = (let y = 1 in z|) in`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.Undefined, - key: "z", - }, - { - kind: ScopeItemKind.KeyValuePair, - key: "y", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - - it(`let x = (let y = 1 in z) in |`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `let x = (let y = 1 in z) in |`, - ); - const expected: AbridgedScope = [ - { - kind: ScopeItemKind.KeyValuePair, - key: "x", - }, - ]; - expectDeepEqual(expectParseErrInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); - }); -}); diff --git a/src/test/libraryTest/inspection/scope/invokeExpression.ts b/src/test/libraryTest/inspection/scope/invokeExpression.ts deleted file mode 100644 index bf8a3920..00000000 --- a/src/test/libraryTest/inspection/scope/invokeExpression.ts +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { expect } from "chai"; -import "mocha"; -import { Inspection } from "../../../.."; -import { Ast, TXorNode } from "../../../../parser"; -import { DefaultSettings } from "../../../../settings"; -import { expectParseErrInspectionOk, expectParseOkInspectionOk, expectTextWithPosition } from "../../../common"; - -function expectNumOfNodeKind(inspected: Inspection.Inspected, expectedKind: Ast.NodeKind, expectedNum: number): void { - let actualNum: number; - - if (inspected.maybeActiveNode === undefined) { - actualNum = -1; - } else { - const nodesOfKind: ReadonlyArray = inspected.maybeActiveNode.ancestry.filter( - (xorNode: TXorNode) => xorNode.node.kind === expectedKind, - ); - actualNum = nodesOfKind.length; - } - - expect(actualNum).to.equal( - expectedNum, - `expected to find ${expectedNum} of ${expectedKind}, but found ${actualNum} instead.`, - ); -} - -function expectNthOfNodeKind(inspected: Inspection.Inspected, nodeKind: Ast.NodeKind, nth: number): X & TXorNode { - if (nth <= 0) { - throw new Error("nth must be > 0"); - } else if (inspected.maybeActiveNode === undefined) { - throw new Error("expected to have an active node"); - } - - let nthFound: number = 0; - for (const xorNode of inspected.maybeActiveNode.ancestry) { - if (xorNode.node.kind === nodeKind) { - nthFound += 1; - if (nth === nthFound) { - return (xorNode as unknown) as X & TXorNode; - } - } - } - - throw new Error(`only found ${nthFound} out of ${nth} ${nodeKind} nodes.`); -} - -describe(`Inspection - InvokeExpression`, () => { - it("single invoke expression, no parameters", () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition("Foo(|)"); - const inspected: Inspection.Inspected = expectParseOkInspectionOk(DefaultSettings, text, position); - expectNumOfNodeKind(inspected, Ast.NodeKind.InvokeExpression, 1); - - expect(inspected.maybeInvokeExpression).not.equal(undefined, "at least one InvokeExpression was found"); - const inspectedInvokeExpr: Inspection.InvokeExpression = inspected.maybeInvokeExpression!; - - const firstInvokeExprNode: TXorNode = expectNthOfNodeKind(inspected, Ast.NodeKind.InvokeExpression, 1); - expect(inspectedInvokeExpr.xorNode.node.id).to.equal(firstInvokeExprNode.node.id); - - expect(inspectedInvokeExpr.maybeName).to.equal("Foo"); - }); - - it("multiple invoke expression, no parameters", () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition("Bar(Foo(|))"); - const inspected: Inspection.Inspected = expectParseOkInspectionOk(DefaultSettings, text, position); - expectNumOfNodeKind(inspected, Ast.NodeKind.InvokeExpression, 2); - - expect(inspected.maybeInvokeExpression).not.equal(undefined, "at least one InvokeExpression was found"); - const inspectedInvokeExpr: Inspection.InvokeExpression = inspected.maybeInvokeExpression!; - - const firstInvokeExprNode: TXorNode = expectNthOfNodeKind(inspected, Ast.NodeKind.InvokeExpression, 1); - expect(inspectedInvokeExpr.xorNode.node.id).to.equal(firstInvokeExprNode.node.id); - - expect(inspectedInvokeExpr.maybeName).to.equal("Foo"); - }); - - it("single invoke expression - Foo(a|)", () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition("Foo(a|)"); - const inspected: Inspection.Inspected = expectParseOkInspectionOk(DefaultSettings, text, position); - expectNumOfNodeKind(inspected, Ast.NodeKind.InvokeExpression, 1); - - expect(inspected.maybeInvokeExpression).not.equal(undefined, "at least one InvokeExpression was found"); - const inspectedInvokeExpr: Inspection.InvokeExpression = inspected.maybeInvokeExpression!; - - const firstInvokeExprNode: TXorNode = expectNthOfNodeKind(inspected, Ast.NodeKind.InvokeExpression, 1); - expect(inspectedInvokeExpr.xorNode.node.id).to.equal(firstInvokeExprNode.node.id); - - expect(inspectedInvokeExpr.maybeArguments).not.equal(undefined, "should be truthy"); - const args: Inspection.InvokeExpressionArgs = inspectedInvokeExpr.maybeArguments!; - expect(args.numArguments).to.equal(1); - expect(args.positionArgumentIndex).to.equal(0); - }); - - it("single invoke expression - Foo(a|,)", () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition("Foo(a|,)"); - const inspected: Inspection.Inspected = expectParseErrInspectionOk(DefaultSettings, text, position); - expectNumOfNodeKind(inspected, Ast.NodeKind.InvokeExpression, 1); - - expect(inspected.maybeInvokeExpression).not.equal(undefined, "at least one InvokeExpression was found"); - const inspectedInvokeExpr: Inspection.InvokeExpression = inspected.maybeInvokeExpression!; - - const firstInvokeExprNode: TXorNode = expectNthOfNodeKind(inspected, Ast.NodeKind.InvokeExpression, 1); - expect(inspectedInvokeExpr.xorNode.node.id).to.equal(firstInvokeExprNode.node.id); - - expect(inspectedInvokeExpr.maybeArguments).not.equal(undefined, "should be truthy"); - const args: Inspection.InvokeExpressionArgs = inspectedInvokeExpr.maybeArguments!; - expect(args.numArguments).to.equal(2); - expect(args.positionArgumentIndex).to.equal(0); - }); - - it("single invoke expression - Foo(a,|)", () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition("Foo(a,|)"); - const inspected: Inspection.Inspected = expectParseErrInspectionOk(DefaultSettings, text, position); - expectNumOfNodeKind(inspected, Ast.NodeKind.InvokeExpression, 1); - - expect(inspected.maybeInvokeExpression).not.equal(undefined, "at least one InvokeExpression was found"); - const inspectedInvokeExpr: Inspection.InvokeExpression = inspected.maybeInvokeExpression!; - - const firstInvokeExprNode: TXorNode = expectNthOfNodeKind(inspected, Ast.NodeKind.InvokeExpression, 1); - expect(inspectedInvokeExpr.xorNode.node.id).to.equal(firstInvokeExprNode.node.id); - - expect(inspectedInvokeExpr.maybeArguments).not.equal(undefined, "should be truthy"); - const args: Inspection.InvokeExpressionArgs = inspectedInvokeExpr.maybeArguments!; - expect(args.numArguments).to.equal(2); - expect(args.positionArgumentIndex).to.equal(1); - }); -}); diff --git a/src/test/libraryTest/inspection/scope/parameter.ts b/src/test/libraryTest/inspection/scope/parameter.ts deleted file mode 100644 index 65fcc211..00000000 --- a/src/test/libraryTest/inspection/scope/parameter.ts +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import "mocha"; -import { Inspection } from "../../../.."; -import { ScopeItemKind } from "../../../../inspection"; -import { Ast } from "../../../../parser"; -import { DefaultSettings } from "../../../../settings"; -import { expectDeepEqual, expectParseOkInspectionOk, expectTextWithPosition } from "../../../common"; - -type AbridgedScope = ReadonlyArray; - -interface AbridgedParameterItem { - readonly key: string; - readonly isOptional: boolean; - readonly isNullable: boolean; - readonly maybeType: Ast.TConstantKind | undefined; -} - -function actualFactoryFn(inspected: Inspection.Inspected): AbridgedScope { - const abridgedScopeItems: AbridgedParameterItem[] = []; - - for (const [key, scopeItem] of inspected.scope.entries()) { - if (scopeItem.kind === ScopeItemKind.Parameter) { - abridgedScopeItems.push({ - key, - isOptional: scopeItem.isOptional, - isNullable: scopeItem.isNullable, - maybeType: scopeItem.maybeType, - }); - } - } - - return abridgedScopeItems; -} - -describe(`Inspection - Scope - Parameter`, () => { - it(`(a, b as number, c as nullable function, optional d, optional e as table) => 1|`, () => { - const [text, position]: [string, Inspection.Position] = expectTextWithPosition( - `(a, b as number, c as nullable function, optional d, optional e as table) => 1|`, - ); - const expected: AbridgedScope = [ - { - key: "a", - isOptional: false, - isNullable: true, - maybeType: undefined, - }, - { - key: "b", - isOptional: false, - isNullable: false, - maybeType: Ast.PrimitiveTypeConstantKind.Number, - }, - { - key: "c", - isOptional: false, - isNullable: true, - maybeType: Ast.PrimitiveTypeConstantKind.Function, - }, - { - key: "d", - isOptional: true, - isNullable: true, - maybeType: undefined, - }, - { - key: "e", - isOptional: true, - isNullable: false, - maybeType: Ast.PrimitiveTypeConstantKind.Table, - }, - ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); - }); -}); diff --git a/src/test/libraryTest/inspection/scope/tokenRange.ts b/src/test/libraryTest/inspection/scope/tokenRange.ts deleted file mode 100644 index dc0c5802..00000000 --- a/src/test/libraryTest/inspection/scope/tokenRange.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import "mocha"; -import { Inspection } from "../../../.."; -import { expectTextWithPosition } from "../../../common"; - -describe(`Inspection - Scope - TokenRange`, () => { - it(`let Function = () => 1, foo = Func|tion() in foo`, () => { - const []: [string, Inspection.Position] = expectTextWithPosition(`let x = (let y = 1 in z) in |`); - }); -}); diff --git a/src/test/libraryTest/inspection/scope/type.ts b/src/test/libraryTest/inspection/type.ts similarity index 55% rename from src/test/libraryTest/inspection/scope/type.ts rename to src/test/libraryTest/inspection/type.ts index 9cf9cca7..36fa9b7a 100644 --- a/src/test/libraryTest/inspection/scope/type.ts +++ b/src/test/libraryTest/inspection/type.ts @@ -2,10 +2,14 @@ // Licensed under the MIT license. import "mocha"; -import { Inspection } from "../../../.."; -import { DefaultSettings } from "../../../../settings"; -import { Type } from "../../../../type"; -import { expectDeepEqual, expectParseOkInspectionOk, expectTextWithPosition } from "../../../common"; +import { Inspection } from "../../.."; +import { CommonError, Result, ResultUtils } from "../../../common"; +import { Position, ScopeItemByKey, ScopeTypeMap, TriedScopeType } from "../../../inspection"; +import { ActiveNode, ActiveNodeUtils } from "../../../inspection/activeNode"; +import { IParserState, NodeIdMap, ParseOk } from "../../../parser"; +import { CommonSettings, DefaultSettings, LexSettings, ParseSettings } from "../../../settings"; +import { Type } from "../../../type"; +import { expectDeepEqual, expectParseOk, expectTextWithPosition } from "../../common"; type AbridgedScopeType = ReadonlyArray; @@ -16,8 +20,52 @@ interface AbridgedScopeTypeElement { readonly isNullable: boolean; } -function actualFactoryFn(inspected: Inspection.Inspected): AbridgedScopeType { - return [...inspected.scopeTypeMap.entries()] +function expectScopeTypeOk( + settings: CommonSettings, + nodeIdMapCollection: NodeIdMap.Collection, + leafNodeIds: ReadonlyArray, + position: Position, +): ScopeTypeMap { + const maybeActiveNode: undefined | ActiveNode = ActiveNodeUtils.maybeActiveNode( + position, + nodeIdMapCollection, + leafNodeIds, + ); + if (!(maybeActiveNode !== undefined)) { + throw new Error(`AssertedFailed: maybeActiveNode !== undefined`); + } + const activeNode: ActiveNode = maybeActiveNode; + + const triedScope: Result = Inspection.tryScopeForRoot( + settings, + nodeIdMapCollection, + leafNodeIds, + activeNode.ancestry, + undefined, + ); + if (!ResultUtils.isOk(triedScope)) { + throw new Error(`AssertFailed: ResultUtils.isOk(triedScope) - ${triedScope.error}`); + } + + const triedScopeType: TriedScopeType = Inspection.tryScopeType(settings, nodeIdMapCollection, triedScope.value); + if (!ResultUtils.isOk(triedScopeType)) { + throw new Error(`AssertFailed: ResultUtils.isOk(triedScopeType) - ${triedScopeType.error}`); + } + + return triedScopeType.value; +} + +function expectParseOkScopeTypeOk( + settings: LexSettings & ParseSettings, + text: string, + position: Position, +): ScopeTypeMap { + const parseOk: ParseOk = expectParseOk(settings, text); + return expectScopeTypeOk(settings, parseOk.nodeIdMapCollection, parseOk.leafNodeIds, position); +} + +function actualFactoryFn(inspected: ScopeTypeMap): AbridgedScopeType { + return [...inspected.entries()] .map(([key, type]) => { return { key, @@ -37,7 +85,7 @@ function expectExpressionType(expression: string, kind: Type.TypeKind, isNullabl isNullable, }, ]; - expectDeepEqual(expectParseOkInspectionOk(DefaultSettings, text, position), expected, actualFactoryFn); + expectDeepEqual(expectParseOkScopeTypeOk(DefaultSettings, text, position), expected, actualFactoryFn); } describe(`Inspection - Scope - Type`, () => { @@ -85,7 +133,7 @@ describe(`Inspection - Scope - Type`, () => { }); }); - describe("abc1232 UnaryExpression", () => { + describe("UnaryExpression", () => { it(`+1`, () => { expectExpressionType(`+1`, Type.TypeKind.Number, false); }); diff --git a/src/test/libraryTest/parser/children.ts b/src/test/libraryTest/parser/children.ts index b1efc287..74dcf612 100644 --- a/src/test/libraryTest/parser/children.ts +++ b/src/test/libraryTest/parser/children.ts @@ -2,9 +2,9 @@ // Licensed under the MIT license. import "mocha"; +import { Task } from "../../.."; import { Ast, IParserState, NodeIdMap } from "../../../parser"; import { DefaultSettings } from "../../../settings"; -import { LexParseOk } from "../../../tasks"; import { expectDeepEqual, expectLexParseOk } from "../../common"; interface ChildIdsByIdEntry { @@ -13,7 +13,7 @@ interface ChildIdsByIdEntry { readonly kind: Ast.NodeKind; } -function acutalFactoryFn(lexParseOk: LexParseOk): ChildIdsByIdEntry[] { +function acutalFactoryFn(lexParseOk: Task.LexParseOk): ChildIdsByIdEntry[] { const actual: ChildIdsByIdEntry[] = []; const astNodeById: NodeIdMap.AstNodeById = lexParseOk.nodeIdMapCollection.astNodeById; diff --git a/src/test/libraryTest/parser/simple.ts b/src/test/libraryTest/parser/simple.ts index 76962ebe..b6ae5e80 100644 --- a/src/test/libraryTest/parser/simple.ts +++ b/src/test/libraryTest/parser/simple.ts @@ -3,11 +3,11 @@ import { expect } from "chai"; import "mocha"; +import { Task } from "../../.."; import { ResultUtils, Traverse } from "../../../common"; import { DefaultTemplates } from "../../../localization"; import { Ast } from "../../../parser"; import { DefaultSettings } from "../../../settings"; -import { LexParseOk } from "../../../tasks"; import { expectLexParseOk } from "../../common"; type AbridgedNode = [Ast.NodeKind, number | undefined]; @@ -21,7 +21,7 @@ interface NthNodeOfKindState extends Traverse.IState { } function collectAbridgeNodeFromAst(text: string): ReadonlyArray { - const lexParseOk: LexParseOk = expectLexParseOk(DefaultSettings, text); + const lexParseOk: Task.LexParseOk = expectLexParseOk(DefaultSettings, text); const state: CollectAbridgeNodeState = { localizationTemplates: DefaultTemplates, result: [], @@ -48,7 +48,7 @@ function collectAbridgeNodeFromAst(text: string): ReadonlyArray { } function expectNthNodeOfKind(text: string, nodeKind: Ast.NodeKind, nthRequired: number): N & Ast.TNode { - const lexParseOk: LexParseOk = expectLexParseOk(DefaultSettings, text); + const lexParseOk: Task.LexParseOk = expectLexParseOk(DefaultSettings, text); const state: NthNodeOfKindState = { localizationTemplates: DefaultTemplates, result: undefined, diff --git a/src/test/resourceTest/benchmark.ts b/src/test/resourceTest/benchmark.ts index ead86e7c..e661f191 100644 --- a/src/test/resourceTest/benchmark.ts +++ b/src/test/resourceTest/benchmark.ts @@ -5,13 +5,12 @@ import performanceNow = require("performance-now"); import "mocha"; +import { Task } from "../.."; import { ResultUtils } from "../../common"; import { LexerSnapshot } from "../../lexer"; import { DefaultTemplates } from "../../localization"; -import { IParser, IParserState, IParserStateUtils } from "../../parser"; -import { CombinatorialParser } from "../../parser/parsers"; +import { IParser, IParserState, IParserStateUtils, Parser } from "../../parser"; import { ParseSettings, Settings } from "../../settings"; -import { TriedLexParse } from "../../tasks"; import { BenchmarkParser, BenchmarkState, FunctionTimestamp } from "./benchmarkParser"; import * as path from "path"; @@ -48,14 +47,14 @@ function createRecurisveDescentBenchmarkState( settings: ParseSettings, lexerSnapshot: LexerSnapshot, ): BenchmarkState { - return createBenchmarkState(settings, lexerSnapshot, CombinatorialParser); + return createBenchmarkState(settings, lexerSnapshot, Parser.CombinatorialParser); } function createCombinatorialBenchmarkState( settings: ParseSettings, lexerSnapshot: LexerSnapshot, ): BenchmarkState { - return createBenchmarkState(settings, lexerSnapshot, CombinatorialParser); + return createBenchmarkState(settings, lexerSnapshot, Parser.CombinatorialParser); } function createBenchmarkState( @@ -97,7 +96,7 @@ function parseAllFiles(settings: Settings, parserName: string): // tslint:disable-next-line: no-console console.log(`\tRun ${index} of ${NumberOfRunsPerFile}`); } - const triedLexParse: TriedLexParse = FileUtils.tryLexParse(settings, filePath); + const triedLexParse: Task.TriedLexParse = FileUtils.tryLexParse(settings, filePath); if (!ResultUtils.isOk(triedLexParse)) { throw triedLexParse.error; } diff --git a/src/test/resourceTest/lexParse.ts b/src/test/resourceTest/lexParse.ts index 59ece5f1..4e422231 100644 --- a/src/test/resourceTest/lexParse.ts +++ b/src/test/resourceTest/lexParse.ts @@ -1,8 +1,8 @@ import "mocha"; +import { Task } from "../.."; import { ResultUtils } from "../../common"; import { IParser, Parser } from "../../parser"; import { DefaultSettings, Settings } from "../../settings"; -import { TriedLexParse } from "../../tasks"; import * as path from "path"; import * as FileUtils from "../fileUtils"; @@ -35,7 +35,7 @@ function parseAllFiles(settings: Settings, parserName: string): void { const testName: string = testNameFromFilePath(filePath); it(testName, () => { - const triedLexParse: TriedLexParse = FileUtils.tryLexParse(settings, filePath); + const triedLexParse: Task.TriedLexParse = FileUtils.tryLexParse(settings, filePath); if (!ResultUtils.isOk(triedLexParse)) { throw triedLexParse.error; } diff --git a/src/type/inspector/functionExpressionInspector.ts b/src/type/inspector/functionExpressionInspector.ts index 98f0060e..1aa01888 100644 --- a/src/type/inspector/functionExpressionInspector.ts +++ b/src/type/inspector/functionExpressionInspector.ts @@ -90,7 +90,7 @@ function functionParameterXorNodes( return maybeWrappedContent === undefined ? [] - : NodeIdMapIterator.arrayWrapperXorNodes(nodeIdMapCollection, maybeWrappedContent); + : NodeIdMapIterator.arrayWrapperCsvXorNodes(nodeIdMapCollection, maybeWrappedContent); } function examineParameter(