Skip to content

Commit

Permalink
Replaced Bind with Input in table parsing and evaluation.
Browse files Browse the repository at this point in the history
  • Loading branch information
amyjko committed Jun 30, 2024
1 parent 30b34e6 commit 0010660
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 68 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Dates are in `YYYY-MM-DD` format and versions are in [semantic versioning](http:
- [#504](https://github.com/wordplaydev/wordplay/issues/504). Account for non-fixed-width characters in caret positioning.
- [#488](https://github.com/wordplaydev/wordplay/issues/488). Added animations off indicator on stage.
- [#500](https://github.com/wordplaydev/wordplay/issues/500). Improved explanation when there's a space between an evaluation's name and inputs.
- [#455](https://github.com/wordplaydev/wordplay/issues/455). Replaced `Bind`s with `Input`'s in `Evaluate` to prevent invalid bind metadata in evaluations.
- [#455](https://github.com/wordplaydev/wordplay/issues/455). Replaced `Bind`s with `Input`'s in `Evaluate` and table operations to prevent invalid bind metadata in evaluations.

### Maintenance

Expand Down
1 change: 0 additions & 1 deletion src/components/editor/RowView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import type Row from '@nodes/Row';
import NodeSequenceView from './NodeSequenceView.svelte';
import NodeView from './NodeView.svelte';
export let node: Row;
</script>

Expand Down
2 changes: 1 addition & 1 deletion src/conflicts/InvalidRow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default class InvalidRow extends Conflict {
explanation: (locales: Locales) =>
concretize(
locales,
locales.get((l) => l.node.Row.conflict.InvalidRow)
locales.get((l) => l.node.Row.conflict.InvalidRow),
),
},
};
Expand Down
10 changes: 5 additions & 5 deletions src/conflicts/MissingCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ export default class MissingCell extends Conflict {
concretize(
locales,
locales.get(
(l) => l.node.Row.conflict.MissingCell.primary
(l) => l.node.Row.conflict.MissingCell.primary,
),
new NodeRef(
this.column,
locales,
context,
locales.getName(this.column.names)
)
locales.getName(this.column.names),
),
),
},
secondary: {
Expand All @@ -44,9 +44,9 @@ export default class MissingCell extends Conflict {
concretize(
locales,
locales.get(
(l) => l.node.Row.conflict.MissingCell.secondary
(l) => l.node.Row.conflict.MissingCell.secondary,
),
new NodeRef(this.row, locales, context)
new NodeRef(this.row, locales, context),
),
},
};
Expand Down
5 changes: 5 additions & 0 deletions src/nodes/Input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ export default class Input extends SimpleExpression {
return this.value.getType(context);
}

/** Never constant, as we always reevaluate functions. */
isConstant(): boolean {
return false;
}

getDependencies(): Expression[] {
return [this.value];
}
Expand Down
7 changes: 4 additions & 3 deletions src/nodes/Insert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { INSERT_SYMBOL, TABLE_CLOSE_SYMBOL } from '../parser/Symbols';
import Sym from './Sym';
import ExpressionPlaceholder from './ExpressionPlaceholder';
import type Locales from '../locale/Locales';
import Input from './Input';

export default class Insert extends Expression {
readonly table: Expression;
Expand Down Expand Up @@ -132,12 +133,12 @@ export default class Insert extends Expression {
// Rows can either be all unnamed and provide values for every column or they can be selectively named,
// but must provide a value for all non-default columns. No other format is allowed.
// Additionally, all values must match their column's types.
if (this.row.allBinds()) {
if (this.row.cells.every((c) => c instanceof Input)) {
// Ensure every bind is a valid column.
const matchedColumns = [];
for (const cell of this.row.cells) {
if (cell instanceof Bind) {
const column = tableType.getColumnNamed(cell.getNames()[0]);
if (cell instanceof Input) {
const column = tableType.getColumnNamed(cell.getName());
if (column === undefined)
conflicts.push(new UnknownColumn(tableType, cell));
else {
Expand Down
26 changes: 15 additions & 11 deletions src/nodes/Row.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Grammar, Replacement } from './Node';
import Token from './Token';
import Bind from './Bind';
import Expression from './Expression';
import Glyphs from '../lore/Glyphs';
import Purpose from '../concepts/Purpose';
Expand All @@ -15,13 +14,18 @@ import ValueException from '../values/ValueException';
import StructureValue from '../values/StructureValue';
import { TABLE_CLOSE_SYMBOL, TABLE_OPEN_SYMBOL } from '../parser/Symbols';
import type Locales from '../locale/Locales';
import Input from './Input';

export default class Row extends Node {
readonly open: Token;
readonly cells: Expression[];
readonly cells: (Input | Expression)[];
readonly close: Token | undefined;

constructor(open: Token, cells: Expression[], close: Token | undefined) {
constructor(
open: Token,
cells: (Input | Expression)[],
close: Token | undefined,
) {
super();

this.open = open;
Expand All @@ -35,7 +39,7 @@ export default class Row extends Node {
return new Row(
new Token(TABLE_OPEN_SYMBOL, Sym.TableOpen),
cells,
new Token(TABLE_CLOSE_SYMBOL, Sym.TableClose)
new Token(TABLE_CLOSE_SYMBOL, Sym.TableClose),
);
}

Expand All @@ -52,7 +56,7 @@ export default class Row extends Node {
node(Sym.Select),
node(Sym.Insert),
node(Sym.Delete),
node(Sym.Update)
node(Sym.Update),
),
},
{ name: 'cells', kind: list(true, node(Expression)), space: true },
Expand All @@ -64,20 +68,20 @@ export default class Row extends Node {
return new Row(
this.replaceChild('open', this.open, replace),
this.replaceChild('cells', this.cells, replace),
this.replaceChild('close', this.close, replace)
this.replaceChild('close', this.close, replace),
) as this;
}

getPurpose() {
return Purpose.Bind;
}

allBinds() {
return this.cells.every((cell) => cell instanceof Bind);
getDependencies() {
return [...this.cells];
}

allExpressions() {
return this.cells.every((cell) => !(cell instanceof Bind));
return this.cells.every((cell) => !(cell instanceof Input));
}

computeConflicts() {
Expand All @@ -98,13 +102,13 @@ export function getRowFromValues(
evaluator: Evaluator,
creator: EvaluationNode,
table: TableType,
values: Value[]
values: Value[],
) {
const evaluation = new Evaluation(
evaluator,
creator,
table.definition,
evaluator.getCurrentEvaluation()
evaluator.getCurrentEvaluation(),
);

for (let c = 0; c < table.columns.length; c++) {
Expand Down
8 changes: 5 additions & 3 deletions src/nodes/TableLiteral.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,11 @@ export default class TableLiteral extends Expression {
* Is a binding enclosure of its columns and rows, because it defines columns.
* */
getScopeOfChild(child: Node, context: Context): Node | undefined {
return this.rows.includes(child as Row)
? this.type
: this.getParent(context);
return child instanceof Row
? this.rows.includes(child)
? this.type
: this.getParent(context)
: undefined;
}

evaluateTypeGuards(current: TypeSet, guard: GuardContext) {
Expand Down
57 changes: 20 additions & 37 deletions src/nodes/Update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { TABLE_CLOSE_SYMBOL, UPDATE_SYMBOL } from '../parser/Symbols';
import Sym from './Sym';
import ExpressionPlaceholder from './ExpressionPlaceholder';
import type Locales from '../locale/Locales';
import Input from './Input';

type UpdateState = { table: TableValue; index: number; rows: StructureValue[] };

Expand Down Expand Up @@ -147,25 +148,12 @@ export default class Update extends Expression {
}

this.row.cells.forEach((cell) => {
// The columns in an update must be binds with expressions.
if (
!(
cell instanceof Bind &&
cell.value !== undefined &&
cell.names.names.length === 1
)
)
// The columns in an update must be inputs
if (!(cell instanceof Input))
conflicts.push(new ExpectedColumnBind(this, cell));
else if (tableType instanceof TableType) {
const alias =
cell instanceof Bind && cell.names.names.length > 0
? cell.names.names[0]
: undefined;
const name = alias === undefined ? undefined : alias.getName();
const columnType =
name === undefined
? undefined
: tableType.getColumnNamed(name);
const name = cell.getName();
const columnType = tableType.getColumnNamed(name);
// The named table column must exist.
if (columnType === undefined)
conflicts.push(new UnknownColumn(tableType, cell));
Expand Down Expand Up @@ -208,13 +196,12 @@ export default class Update extends Expression {
return this.table.getType(context);
}

getDefinitions(node: Node, context: Context): Definition[] {
node;
getDefinitions(_: Node, context: Context): Definition[] {
const type = this.table.getType(context);
if (type instanceof TableType)
return type.columns
.filter((col) => col instanceof Bind)
.map((col) => col) as Bind[];
.map((col) => col);
else return [];
}

Expand All @@ -231,15 +218,13 @@ export default class Update extends Expression {
: new AnyType();

// Get the binds
const binds = this.row.cells;
const inputs = this.row.cells.filter(
(c): c is Input => c instanceof Input,
);

// Get the update steps
const updates = binds
.map((bind) =>
bind instanceof Bind && bind.value
? bind.value.compile(evaluator, context)
: [],
)
const updates = inputs
.map((input) => input.value.compile(evaluator, context))
.flat();

/** A derived function based on the query, used to evaluate each row of the table. */
Expand Down Expand Up @@ -267,12 +252,10 @@ export default class Update extends Expression {
);
// Get the values computed
const values: Value[] = [];
for (const bind of binds) {
if (bind instanceof Bind && bind.value) {
const value = evaluation.popValue(this, undefined);
if (value instanceof ExceptionValue) return value;
values.unshift(value);
}
for (let count = 0; count < inputs.length; count++) {
const value = evaluation.popValue(this, undefined);
if (value instanceof ExceptionValue) return value;
values.unshift(value);
}
// Get the query result
const match = evaluation.popValue(
Expand All @@ -282,17 +265,17 @@ export default class Update extends Expression {
if (!(match instanceof BoolValue)) return match;
// Not a query match? Don't modify the row.
if (match.bool === false) return row;
// Otherwise, refine the row with the updtes
// Otherwise, refine the row with the updates
else {
let newRow: StructureValue = row;
for (const bind of binds) {
if (bind instanceof Bind && bind.value) {
for (const input of inputs) {
if (input instanceof Input) {
const value = values.shift();
const revised: StructureValue | undefined =
value
? newRow.withValue(
this,
bind.names.getNames()[0],
input.getName(),
value,
)
: undefined;
Expand Down
7 changes: 7 additions & 0 deletions src/parser/parseBind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,10 @@ export function nextIsBind(tokens: Tokens, expectValue: boolean): boolean {
bind.names.hasALanguageTag())
);
}

export function nextIsInput(tokens: Tokens): boolean {
return (
tokens.nextIsOneOf(Sym.Name, Sym.Operator) &&
tokens.afterNextIs(Sym.Bind)
);
}
11 changes: 5 additions & 6 deletions src/parser/parseExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Sym from '../nodes/Sym';
import This from '../nodes/This';
import UnaryEvaluate from '../nodes/UnaryEvaluate';
import UnparsableExpression from '../nodes/UnparsableExpression';
import parseBind, { nextIsBind, parseNames } from './parseBind';
import parseBind, { nextIsBind, nextIsInput, parseNames } from './parseBind';
import type Tokens from './Tokens';
import ListLiteral from '@nodes/ListLiteral';
import type Bind from '@nodes/Bind';
Expand Down Expand Up @@ -612,7 +612,7 @@ function parseRow(tokens: Tokens, expected: Sym = Sym.TableOpen): Row {
// Don't allow reactions on row values.
tokens.pushReactionAllowed(false);

const cells: (Bind | Expression)[] = [];
const cells: (Input | Expression)[] = [];
// Read the cells.
tokens.untilDo(
() =>
Expand All @@ -622,8 +622,8 @@ function parseRow(tokens: Tokens, expected: Sym = Sym.TableOpen): Row {
!tokens.nextHasPrecedingLineBreak(),
() =>
cells.push(
nextIsBind(tokens, true)
? parseBind(tokens)
nextIsInput(tokens)
? parseInput(tokens)
: parseExpression(tokens),
),
);
Expand Down Expand Up @@ -828,8 +828,7 @@ function parseEvaluate(left: Expression, tokens: Tokens): Evaluate {
tokens.nextIsnt(Sym.EvalClose),
() =>
inputs.push(
tokens.nextIsOneOf(Sym.Name, Sym.Operator) &&
tokens.afterNextIs(Sym.Bind)
nextIsInput(tokens)
? parseInput(tokens)
: parseExpression(tokens),
),
Expand Down

0 comments on commit 0010660

Please sign in to comment.