diff --git a/package-lock.json b/package-lock.json index f6010dbc..0808ed45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@microsoft/powerquery-parser", - "version": "1.0.0", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@microsoft/powerquery-parser", - "version": "1.0.0", + "version": "2.0.0", "license": "MIT", "dependencies": { "grapheme-splitter": "^1.0.4", diff --git a/package.json b/package.json index 08b9df1e..198f675c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/powerquery-parser", - "version": "1.0.0", + "version": "2.0.0", "description": "A parser for the Power Query/M formula language.", "author": "Microsoft", "license": "MIT", diff --git a/src/powerquery-parser/language/type/type.ts b/src/powerquery-parser/language/type/type.ts index ad4745ce..8e7899cc 100644 --- a/src/powerquery-parser/language/type/type.ts +++ b/src/powerquery-parser/language/type/type.ts @@ -307,7 +307,7 @@ export type DefinedTable = IExtendedType & FieldSpecificationList & { readonly kind: TypeKind.Table; readonly extendedKind: ExtendedTypeKind.DefinedTable; - readonly rows: ReadonlyArray; + readonly rows?: ReadonlyArray; }; export type FunctionType = IExtendedType & diff --git a/src/powerquery-parser/language/type/typeUtils/factories.ts b/src/powerquery-parser/language/type/typeUtils/factories.ts index 77d58d43..53a83f6b 100644 --- a/src/powerquery-parser/language/type/typeUtils/factories.ts +++ b/src/powerquery-parser/language/type/typeUtils/factories.ts @@ -97,19 +97,20 @@ export function definedRecord( } /** - * Creates a defined table with exact rows. + * Creates a defined table with known fields and optional exact rows. * - * Each row is asserted to contain exactly the declared fields and field types compatible with the table definition. + * Omitted rows indicate that the table's rows are unknown, while an empty array represents an exact empty table. + * Each supplied row is asserted to contain exactly the declared fields and field types compatible with the table definition. * @throws CommonError.InvariantError if a row does not satisfy those requirements. */ export function definedTable( isNullable: boolean, fields: Type.OrderedFields, - rows: ReadonlyArray, + rows?: ReadonlyArray, ): Type.DefinedTable { const fieldNames: ReadonlyArray = [...fields.keys()]; - for (const [rowIndex, row] of rows.entries()) { + for (const [rowIndex, row] of rows?.entries() ?? []) { Assert.isTrue( MapUtils.hasKeys(row, fieldNames) && row.size === fields.size, `row fields do not match table fields`, diff --git a/src/powerquery-parser/language/type/typeUtils/isEqualType.ts b/src/powerquery-parser/language/type/typeUtils/isEqualType.ts index ddaccbbf..2f440e76 100644 --- a/src/powerquery-parser/language/type/typeUtils/isEqualType.ts +++ b/src/powerquery-parser/language/type/typeUtils/isEqualType.ts @@ -183,12 +183,12 @@ export function isEqualDefinedTable(left: Type.DefinedTable, right: Type.Defined } function isEqualDefinedTableRows( - left: ReadonlyArray, - right: ReadonlyArray, + left: ReadonlyArray | undefined, + right: ReadonlyArray | undefined, ): boolean { if (left === right) { return true; - } else if (left.length !== right.length) { + } else if (left === undefined || right === undefined || left.length !== right.length) { return false; } diff --git a/src/test/libraryTest/language/typeUtils/isEqualType.test.ts b/src/test/libraryTest/language/typeUtils/isEqualType.test.ts index 1a69e9c9..9e6c1811 100644 --- a/src/test/libraryTest/language/typeUtils/isEqualType.test.ts +++ b/src/test/libraryTest/language/typeUtils/isEqualType.test.ts @@ -99,5 +99,21 @@ describe(`TypeUtils.isEqualType`, () => { expected: false, }); }); + + it(`unknown rows equal unknown rows`, () => { + runTest({ + left: TypeUtils.definedTable(false, fields), + right: TypeUtils.definedTable(false, fields), + expected: true, + }); + }); + + it(`unknown rows differ from exact empty rows`, () => { + runTest({ + left: TypeUtils.definedTable(false, fields), + right: TypeUtils.definedTable(false, fields, []), + expected: false, + }); + }); }); }); diff --git a/src/test/libraryTest/language/typeUtils/typeUtils.test.ts b/src/test/libraryTest/language/typeUtils/typeUtils.test.ts index 71726b71..940c94a8 100644 --- a/src/test/libraryTest/language/typeUtils/typeUtils.test.ts +++ b/src/test/libraryTest/language/typeUtils/typeUtils.test.ts @@ -247,6 +247,12 @@ describe(`TypeUtils`, () => { expect(type.rows).to.equal(rows); }); + it(`allows unknown rows`, () => { + const type: Type.DefinedTable = TypeUtils.definedTable(false, fields); + + expect(type.rows).to.be.undefined; + }); + it(`accepts null for a nullable column`, () => { const nullableFields: Type.OrderedFields = new OrderedMap([["Value", Type.NullableNumberInstance]]);