Skip to content

Commit

Permalink
allow partial null comparison
Browse files Browse the repository at this point in the history
  • Loading branch information
mtoy-googly-moogly committed Jan 13, 2025
1 parent f904f61 commit b1458dd
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 40 deletions.
18 changes: 0 additions & 18 deletions packages/malloy/src/lang/ast/expressions/expr-compare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,6 @@ const compareTypes = {
'>': [TDU.numberT, TDU.stringT, TDU.dateT, TDU.timestampT],
};

export class ExprIsNull extends ExpressionDef {
elementType = 'is null';
constructor(
readonly expr: ExpressionDef,
readonly eq: boolean
) {
super();
this.has({expr});
}

getExpression(fs: FieldSpace): ExprValue {
const expr = this.expr.getExpression(fs);
expr.type = 'boolean';
expr.value = {node: this.eq ? 'is-null' : 'is-not-null', e: expr.value};
return expr;
}
}

export class ExprCompare extends BinaryBoolean<CompareMalloyOperator> {
elementType = 'a<=>b';
constructor(
Expand Down
61 changes: 53 additions & 8 deletions packages/malloy/src/lang/ast/expressions/expr-null.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@

import {BinaryMalloyOperator, FieldSpace} from '..';
import {ExprValue, literalExprValue} from '../types/expr-value';
import {ExpressionDef} from '../types/expression-def';
import {ATNodeType, ExpressionDef} from '../types/expression-def';

function doIsNull(fs: FieldSpace, op: string, expr: ExpressionDef): ExprValue {
const nullCmp = expr.getExpression(fs);
nullCmp.type = 'boolean';
nullCmp.value = {
node: op === '=' ? 'is-null' : 'is-not-null',
e: nullCmp.value,
};
return nullCmp;
}

export class ExprNULL extends ExpressionDef {
elementType = 'NULL';
Expand All @@ -41,14 +51,49 @@ export class ExprNULL extends ExpressionDef {
left: ExpressionDef
): ExprValue {
if (op === '!=' || op === '=') {
const expr = left.getExpression(fs);
expr.type = 'boolean';
expr.value = {
node: op === '=' ? 'is-null' : 'is-not-null',
e: expr.value,
};
return expr;
return doIsNull(fs, op, left);
}
return super.apply(fs, op, left, true);
}
}

export class PartialIsNull extends ExpressionDef {
elementType = '<=> NULL';
constructor(readonly op: '=' | '!=') {
super();
}

apply(fs: FieldSpace, op: string, expr: ExpressionDef): ExprValue {
return doIsNull(fs, op, expr);
}

requestExpression(_fs: FieldSpace): ExprValue | undefined {
return undefined;
}

getExpression(_fs: FieldSpace): ExprValue {
return this.loggedErrorExpr(
'partial-as-value',
'Partial null check does not have a value'
);
}

atNodeType(): ATNodeType {
return ATNodeType.Partial;
}
}

export class ExprIsNull extends ExpressionDef {
elementType = 'is null';
constructor(
readonly expr: ExpressionDef,
readonly op: '=' | '!='
) {
super();
this.has({expr});
}

getExpression(fs: FieldSpace): ExprValue {
return doIsNull(fs, this.op, this.expr);
}
}
14 changes: 12 additions & 2 deletions packages/malloy/src/lang/grammar/MalloyParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -624,9 +624,19 @@ fieldExpr
| ungroup OPAREN fieldExpr (COMMA fieldName)* CPAREN # exprUngroup
;

partialCompare
: compareOp fieldExpr
;

partialTest
: partialCompare
| IS NOT? NULL
;

partialAllowedFieldExpr
: OPAREN compareOp? fieldExpr CPAREN
| compareOp? fieldExpr
: partialTest
| OPAREN partialTest CPAREN
| fieldExpr
;

fieldExprList
Expand Down
43 changes: 31 additions & 12 deletions packages/malloy/src/lang/malloy-to-ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1273,21 +1273,40 @@ export class MalloyToAST
return this.astAt(new ast.ExprCoalesce(left, right), pcx);
}

visitPartialCompare(pcx: parse.PartialCompareContext): ast.PartialCompare {
const partialOp = pcx.compareOp().text;
if (ast.isComparison(partialOp)) {
return this.astAt(
new ast.PartialCompare(partialOp, this.getFieldExpr(pcx.fieldExpr())),
pcx
);
}
throw this.internalError(
pcx,
`partial comparison '${partialOp}' not recognized`
);
}

visitPartialTest(pcx: parse.PartialTestContext): ast.ExpressionDef {
const cmp = pcx.partialCompare();
if (cmp) {
return this.visitPartialCompare(cmp);
}
return this.astAt(new ast.PartialIsNull(pcx.NOT() ? '!=' : '='), pcx);
}

visitPartialAllowedFieldExpr(
pcx: parse.PartialAllowedFieldExprContext
): ast.ExpressionDef {
const fieldExpr = this.getFieldExpr(pcx.fieldExpr());
const partialOp = pcx.compareOp()?.text;
if (partialOp) {
if (ast.isComparison(partialOp)) {
return this.astAt(new ast.PartialCompare(partialOp, fieldExpr), pcx);
}
throw this.internalError(
pcx,
`partial comparison '${partialOp}' not recognized`
);
const exprCx = pcx.fieldExpr();
if (exprCx) {
return this.getFieldExpr(exprCx);
}
const partialCx = pcx.partialTest();
if (partialCx) {
return this.visitPartialTest(partialCx);
}
return fieldExpr;
throw this.internalError(pcx, 'impossible partial');
}

visitExprString(pcx: parse.ExprStringContext): ast.ExprString {
Expand Down Expand Up @@ -2145,7 +2164,7 @@ export class MalloyToAST
visitExprNullCheck(pcx: parse.ExprNullCheckContext): ast.ExprIsNull {
const expr = pcx.fieldExpr();
return this.astAt(
new ast.ExprIsNull(this.getFieldExpr(expr), pcx.NOT() === undefined),
new ast.ExprIsNull(this.getFieldExpr(expr), pcx.NOT() ? '!=' : '='),
pcx
);
}
Expand Down

0 comments on commit b1458dd

Please sign in to comment.