Skip to content

Commit

Permalink
Make TypeDesc contain/be a type interface (#1976)
Browse files Browse the repository at this point in the history
* better parse errors for records and arrays

* Change TypeDesc to BE a TypeDef

* all hail our benevolent protectors

* mini review

* go ahead and go back to 'turtle'

* get cascade type correct

* move the def util to TDU

* all hail

* cleanup

* make TDU a normal module

* docs update
  • Loading branch information
mtoy-googly-moogly authored Oct 28, 2024
1 parent 30d8553 commit b0e21f8
Show file tree
Hide file tree
Showing 66 changed files with 770 additions and 700 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import {ExpressionValueType} from '../../model';
import {LeafExpressionType} from '../../model/malloy_types';
import {
DefinitionBlueprint,
DialectFunctionOverloadDef,
Expand Down Expand Up @@ -505,7 +505,7 @@ const first_value: DefinitionFor<Standard['first_value']> = {
impl: {function: 'FIRST_VALUE', needsWindowOrderBy: true},
};

const LAG_TYPES: ExpressionValueType[] = [
const LAG_TYPES: LeafExpressionType[] = [
'string',
'number',
'timestamp',
Expand Down
97 changes: 32 additions & 65 deletions packages/malloy/src/dialect/functions/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@

import {
FunctionParameterDef,
FieldValueType,
TypeDesc,
Expr,
FunctionParamTypeDesc,
GenericSQLExpr,
ExpressionValueType,
LeafExpressionType,
} from '../../model/malloy_types';
import {SQLExprElement} from '../../model/utils';

Expand Down Expand Up @@ -140,70 +139,38 @@ export function makeParam(
return {param: param(name, ...allowedTypes), arg: arg(name)};
}

export function maxScalar(dataType: FieldValueType): TypeDesc {
return {
dataType,
expressionType: 'scalar',
evalSpace: 'input',
};
export function maxScalar(type: LeafExpressionType): TypeDesc {
return {type, expressionType: 'scalar', evalSpace: 'input'};
}

export function maxAggregate(dataType: FieldValueType): TypeDesc {
return {
dataType,
expressionType: 'aggregate',
evalSpace: 'input',
};
export function maxAggregate(type: LeafExpressionType): TypeDesc {
return {type, expressionType: 'aggregate', evalSpace: 'input'};
}

export function anyExprType(dataType: FieldValueType): FunctionParamTypeDesc {
return {
dataType,
expressionType: undefined,
evalSpace: 'input',
};
export function anyExprType(type: LeafExpressionType): FunctionParamTypeDesc {
return {type, expressionType: undefined, evalSpace: 'input'};
}

export function maxUngroupedAggregate(
dataType: FieldValueType
type: LeafExpressionType
): FunctionParamTypeDesc {
return {
dataType,
expressionType: 'ungrouped_aggregate',
evalSpace: 'input',
};
return {type, expressionType: 'ungrouped_aggregate', evalSpace: 'input'};
}

export function maxAnalytic(dataType: FieldValueType): FunctionParamTypeDesc {
return {
dataType,
expressionType: 'aggregate_analytic',
evalSpace: 'input',
};
export function maxAnalytic(type: LeafExpressionType): FunctionParamTypeDesc {
return {type, expressionType: 'aggregate_analytic', evalSpace: 'input'};
}

export function minScalar(dataType: FieldValueType): TypeDesc {
return {
dataType,
expressionType: 'scalar',
evalSpace: 'input',
};
export function minScalar(type: LeafExpressionType): TypeDesc {
return {type, expressionType: 'scalar', evalSpace: 'input'};
}

export function minAggregate(dataType: FieldValueType): TypeDesc {
return {
dataType,
expressionType: 'aggregate',
evalSpace: 'input',
};
export function minAggregate(type: LeafExpressionType): TypeDesc {
return {type, expressionType: 'aggregate', evalSpace: 'input'};
}

export function minAnalytic(dataType: FieldValueType): TypeDesc {
return {
dataType,
expressionType: 'scalar_analytic',
evalSpace: 'input',
};
export function minAnalytic(type: LeafExpressionType): TypeDesc {
return {type, expressionType: 'scalar_analytic', evalSpace: 'input'};
}

export function overload(
Expand Down Expand Up @@ -235,13 +202,13 @@ export function overload(
export type TypeDescBlueprint =
// default for return type is min scalar
// default for param type is any expression type (max input)
| ExpressionValueType
| LeafExpressionType
| {generic: string}
| {literal: ExpressionValueType | {generic: string}}
| {constant: ExpressionValueType | {generic: string}}
| {dimension: ExpressionValueType | {generic: string}}
| {measure: ExpressionValueType | {generic: string}}
| {calculation: ExpressionValueType | {generic: string}};
| {literal: LeafExpressionType | {generic: string}}
| {constant: LeafExpressionType | {generic: string}}
| {dimension: LeafExpressionType | {generic: string}}
| {measure: LeafExpressionType | {generic: string}}
| {calculation: LeafExpressionType | {generic: string}};

type ParamTypeBlueprint =
| TypeDescBlueprint
Expand All @@ -251,7 +218,7 @@ type ParamTypeBlueprint =
export interface SignatureBlueprint {
// today only one generic is allowed, but if we need more
// we could change this to `{[name: string]: ExpressionValueType[]}`
generic?: [string, ExpressionValueType[]];
generic?: [string, LeafExpressionType[]];
takes: {[name: string]: ParamTypeBlueprint};
returns: TypeDescBlueprint;
supportsOrderBy?: boolean | 'only_default';
Expand Down Expand Up @@ -303,8 +270,8 @@ export type OverrideMap = {
};

function removeGeneric(
type: ExpressionValueType | {generic: string},
generic: {name: string; type: ExpressionValueType} | undefined
type: LeafExpressionType | {generic: string},
generic: {name: string; type: LeafExpressionType} | undefined
) {
if (typeof type === 'string') {
return type;
Expand All @@ -317,7 +284,7 @@ function removeGeneric(

function expandReturnTypeBlueprint(
blueprint: TypeDescBlueprint,
generic: {name: string; type: ExpressionValueType} | undefined
generic: {name: string; type: LeafExpressionType} | undefined
): TypeDesc {
if (typeof blueprint === 'string') {
return minScalar(blueprint);
Expand Down Expand Up @@ -366,7 +333,7 @@ function extractParamTypeBlueprints(

function expandParamTypeBlueprint(
blueprint: TypeDescBlueprint,
generic: {name: string; type: ExpressionValueType} | undefined
generic: {name: string; type: LeafExpressionType} | undefined
): FunctionParamTypeDesc {
if (typeof blueprint === 'string') {
return anyExprType(blueprint);
Expand All @@ -387,7 +354,7 @@ function expandParamTypeBlueprint(

function expandParamTypeBlueprints(
blueprints: TypeDescBlueprint[],
generic: {name: string; type: ExpressionValueType} | undefined
generic: {name: string; type: LeafExpressionType} | undefined
) {
return blueprints.map(blueprint =>
expandParamTypeBlueprint(blueprint, generic)
Expand All @@ -401,7 +368,7 @@ function isVariadicParamBlueprint(blueprint: ParamTypeBlueprint): boolean {
function expandParamBlueprint(
name: string,
blueprint: ParamTypeBlueprint,
generic: {name: string; type: ExpressionValueType} | undefined
generic: {name: string; type: LeafExpressionType} | undefined
): FunctionParameterDef {
return {
name,
Expand All @@ -415,7 +382,7 @@ function expandParamBlueprint(

function expandParamsBlueprints(
blueprints: {[name: string]: ParamTypeBlueprint},
generic: {name: string; type: ExpressionValueType} | undefined
generic: {name: string; type: LeafExpressionType} | undefined
) {
const paramsArray = Object.entries(blueprints);
return paramsArray.map(blueprint =>
Expand Down Expand Up @@ -522,7 +489,7 @@ function expandImplBlueprint(blueprint: DefinitionBlueprint): {

function expandOneBlueprint(
blueprint: DefinitionBlueprint,
generic?: {name: string; type: ExpressionValueType}
generic?: {name: string; type: LeafExpressionType}
): DialectFunctionOverloadDef {
return {
returnType: expandReturnTypeBlueprint(blueprint.returns, generic),
Expand Down
32 changes: 32 additions & 0 deletions packages/malloy/src/doc/typedesc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# TypeDesc

## Types
Mostly in the translator, there is the need for more meta data about expressions than exists in a [FieldDef/QueryFieldDef](fielddef.md). `TypeDesc` is an extension of the type portion of a fielddef, which contains every type that a name lookup, or expression evaluation might result in.

In addition to `AtomicFieldType`s (which can be stored in a column), the other types which cannot be stored in a database column are:

* durations
* regular expression
* join
* view
* null

The `type:` field of a `TypeDesc` is in the same "type space" as [StructDef](structdef.md) and [FieldDef](fielddef.md), with the following weirdnesses

* A joined `SourceDef` will have it's `type:` field match the field version, but the `TypeDesc` will not include the definition
* A view will have `type: 'turtle'` but the `TypeDesc` will not include the definition
* An array or a record **will** include the type definition, because the schema of the contents is part of the type.

## Additional type metadata

A TypeDesc also has an `expressionType` and an `evalSpace` which are used by the translator to generate correct code and also catch a wide variety of errors.

## Other cousins

The types `Parameter` and `FunctionParamTypeDesc` can be used most places where a `TypeDesc` is accepted. A `FunctionParamTypeDesc` also can have an `any` type and a `Parameter` has a narrower set of types.

## TDU

`typedesc-utils.ts`, typically imported `* as TDU`, contains an number of utility functions for dealing with `TypeDesc` types, including pre-made typedescs for all the atomic types.

In previous versions, much of this functionality was accessed with the prefix `FT` which was a remnant of the days when sub expressions were called "fragments"
2 changes: 0 additions & 2 deletions packages/malloy/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,6 @@ export type {
FunctionParameterDef,
ExpressionValueType,
TypeDesc,
FieldValueType,
ExpressionTypeDesc,
FunctionParamTypeDesc,
// used in MalloyError.log
DocumentLocation,
Expand Down
2 changes: 1 addition & 1 deletion packages/malloy/src/lang/ast/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {ExprValue} from './types/expr-value';
*/
export function errorFor(reason: string): ExprValue {
return {
dataType: 'error',
type: 'error',
expressionType: 'scalar',
value: {node: 'error', message: reason},
evalSpace: 'constant',
Expand Down
8 changes: 4 additions & 4 deletions packages/malloy/src/lang/ast/expressions/binary-boolean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
*/

import {errorFor} from '../ast-utils';
import {FT} from '../fragtype-utils';
import * as TDU from '../typedesc-utils';
import {BinaryMalloyOperator, getExprNode} from '../types/binary_operators';
import {ExprValue, computedExprValue} from '../types/expr-value';
import {ExpressionDef} from '../types/expression-def';
Expand All @@ -32,7 +32,7 @@ export abstract class BinaryBoolean<
opType extends BinaryMalloyOperator,
> extends ExpressionDef {
elementType = 'abstract boolean binary';
legalChildTypes = [FT.boolT];
legalChildTypes = [TDU.boolT];
constructor(
readonly left: ExpressionDef,
readonly op: opType,
Expand All @@ -46,14 +46,14 @@ export abstract class BinaryBoolean<
const right = this.right.getExpression(fs);
if (this.typeCheck(this.left, left) && this.typeCheck(this.right, right)) {
return computedExprValue({
dataType: 'boolean',
dataType: {type: 'boolean'},
value: {
node: getExprNode(this.op),
kids: {left: left.value, right: right.value},
},
from: [left, right],
});
}
return errorFor('logial required boolean');
return errorFor('logical-op expected boolean');
}
}
4 changes: 2 additions & 2 deletions packages/malloy/src/lang/ast/expressions/binary-numeric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import {FT} from '../fragtype-utils';
import * as TDU from '../typedesc-utils';
import {ArithmeticMalloyOperator} from '../types/binary_operators';
import {ExprValue} from '../types/expr-value';
import {ExpressionDef} from '../types/expression-def';
Expand All @@ -37,7 +37,7 @@ export abstract class BinaryNumeric<
readonly right: ExpressionDef
) {
super({left: left, right: right});
this.legalChildTypes = [FT.numberT];
this.legalChildTypes = [TDU.numberT];
}

getExpression(fs: FieldSpace): ExprValue {
Expand Down
4 changes: 2 additions & 2 deletions packages/malloy/src/lang/ast/expressions/boolean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import {ExprValue} from '../types/expr-value';
import {ExpressionDef} from '../types/expression-def';
import {FT} from '../fragtype-utils';
import * as TDU from '../typedesc-utils';

export class Boolean extends ExpressionDef {
elementType = 'boolean literal';
Expand All @@ -32,6 +32,6 @@ export class Boolean extends ExpressionDef {
}

getExpression(): ExprValue {
return {...FT.boolT, value: {node: this.value}};
return {...TDU.boolT, value: {node: this.value}};
}
}
Loading

0 comments on commit b0e21f8

Please sign in to comment.