Skip to content

Commit

Permalink
More consistently concretize name types in binds.
Browse files Browse the repository at this point in the history
  • Loading branch information
amyjko committed Mar 2, 2024
1 parent c731427 commit 8f23b8c
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 54 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Dates are in `YYYY-MM-DD` format and versions are in [semantic versioning](http:
- Better unused bind conflict message.
- Narrowed parsing of structure refinements to avoid conflicting with spreads in lists.
- Account for documented expressions in bind recurrence relations.
- More consistently concretize name types in binds.

## 0.9.34 2024-02-24

Expand Down
10 changes: 4 additions & 6 deletions src/nodes/Bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import type Conflict from '@conflicts/Conflict';
import UnusedBind from '@conflicts/UnusedBind';
import IncompatibleType from '@conflicts/IncompatibleType';
import UnexpectedEtc from '@conflicts/UnexpectedEtc';
import NameType from './NameType';
import StructureType from './StructureType';
import StructureDefinition from './StructureDefinition';
import type Evaluator from '@runtime/Evaluator';
import type Step from '@runtime/Step';
Expand Down Expand Up @@ -50,6 +48,7 @@ import Refer from '../edit/Refer';
import UnknownType from './UnknownType';
import type Locales from '../locale/Locales';
import DocumentedExpression from './DocumentedExpression';
import NameType from './NameType';

export default class Bind extends Expression {
readonly docs?: Docs;
Expand Down Expand Up @@ -482,10 +481,9 @@ export default class Bind extends Expression {

// If the type is a name, and it refers to a structure, resolve it.
// Leave any other names (namely those that refer to type variables) to be concretized by others.
if (type instanceof NameType) {
const nameType = type.getType(context);
if (nameType instanceof StructureType) return nameType;
}
type = type.nodes().some((t) => t instanceof NameType)
? type.concretize(context)
: type;

return type;
}
Expand Down
13 changes: 10 additions & 3 deletions src/nodes/ConversionType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default class ConversionType extends Type {
return new ConversionType(
input,
new Token(CONVERT_SYMBOL, Sym.Convert),
output
output,
);
}

Expand All @@ -48,7 +48,7 @@ export default class ConversionType extends Type {
return new ConversionType(
this.replaceChild('input', this.input, replace),
this.replaceChild('convert', this.convert, replace),
this.replaceChild('output', this.output, replace)
this.replaceChild('output', this.output, replace),
) as this;
}

Expand All @@ -65,10 +65,17 @@ export default class ConversionType extends Type {
this.input.accepts(type.input, context) &&
this.output instanceof Type &&
type.output instanceof Type &&
this.output.accepts(type.output, context)
this.output.accepts(type.output, context),
);
}

concretize(context: Context) {
return ConversionType.make(
this.input.concretize(context),
this.output.concretize(context),
);
}

getBasisTypeName(): BasisTypeName {
return 'conversion';
}
Expand Down
22 changes: 16 additions & 6 deletions src/nodes/FunctionType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default class FunctionType extends Type {
inputs: Bind[],
close: Token | undefined,
output: Type,
definition?: FunctionDefinition
definition?: FunctionDefinition,
) {
super();

Expand All @@ -63,7 +63,7 @@ export default class FunctionType extends Type {
typeVars: TypeVariables | undefined,
inputs: Bind[],
output: Type,
definition?: FunctionDefinition
definition?: FunctionDefinition,
) {
return new FunctionType(
new Token(FUNCTION_SYMBOL, Sym.Function),
Expand All @@ -72,7 +72,7 @@ export default class FunctionType extends Type {
inputs,
new EvalCloseToken(),
output,
definition
definition,
);
}

Expand All @@ -92,7 +92,7 @@ export default class FunctionType extends Type {
this.inputs
.filter((input) => input.isRequired())
.map((input) => input.simplify(context).withoutType()),
ExpressionPlaceholder.make()
ExpressionPlaceholder.make(),
);
}

Expand Down Expand Up @@ -123,7 +123,7 @@ export default class FunctionType extends Type {
this.replaceChild('open', this.open, replace),
this.replaceChild('inputs', this.inputs, replace),
this.replaceChild('close', this.close, replace),
this.replaceChild('output', this.output, replace)
this.replaceChild('output', this.output, replace),
) as this;
}

Expand Down Expand Up @@ -162,6 +162,16 @@ export default class FunctionType extends Type {
});
}

concretize(context: Context) {
return FunctionType.make(
this.types,
this.inputs.map((i) =>
i.type ? i.withType(i.type.concretize(context)) : i,
),
this.output.concretize(context),
);
}

simplify(context: Context) {
// Simplify all of the binds
return new FunctionType(
Expand All @@ -171,7 +181,7 @@ export default class FunctionType extends Type {
this.inputs.map((i) => i.simplify(context)),
this.close,
this.output.simplify(context),
this.definition
this.definition,
);
}

Expand Down
14 changes: 9 additions & 5 deletions src/nodes/ListType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default class ListType extends BasisType {
open: Token,
type: Type | undefined,
close: Token | undefined,
length?: number
length?: number,
) {
super();

Expand All @@ -40,14 +40,14 @@ export default class ListType extends BasisType {
new Token(LIST_OPEN_SYMBOL, Sym.ListOpen),
type,
new Token(LIST_CLOSE_SYMBOL, Sym.ListClose),
length
length,
);
}

static getPossibleNodes(
type: Type | undefined,
node: Node,
selected: boolean
selected: boolean,
) {
return [
ListType.make(),
Expand All @@ -71,7 +71,7 @@ export default class ListType extends BasisType {
return new ListType(
this.replaceChild('open', this.open, replace),
this.replaceChild('type', this.type, replace),
this.replaceChild('close', this.close, replace)
this.replaceChild('close', this.close, replace),
) as this;
}

Expand All @@ -87,10 +87,14 @@ export default class ListType extends BasisType {
(this.type === undefined ||
// If the given type has no type specified, any will do
type.type === undefined ||
this.type.accepts(type.type, context))
this.type.accepts(type.type, context)),
);
}

concretize(context: Context): Type {
return ListType.make(this.type?.concretize(context));
}

generalize(context: Context) {
return ListType.make(this.type?.generalize(context));
}
Expand Down
27 changes: 17 additions & 10 deletions src/nodes/MapType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default class MapType extends BasisType {
key: Type | undefined,
bind: Token,
value: Type | undefined,
close?: Token
close?: Token,
) {
super();

Expand All @@ -48,14 +48,14 @@ export default class MapType extends BasisType {
key,
new BindToken(),
value,
new SetCloseToken()
new SetCloseToken(),
);
}

static getPossibleNodes(
type: Type | undefined,
node: Node,
selected: boolean
selected: boolean,
) {
return [
MapType.make(),
Expand Down Expand Up @@ -85,7 +85,7 @@ export default class MapType extends BasisType {
this.replaceChild('key', this.key, replace),
this.replaceChild('bind', this.bind, replace),
this.replaceChild('value', this.value, replace),
this.replaceChild('close', this.close, replace)
this.replaceChild('close', this.close, replace),
) as this;
}

Expand All @@ -111,14 +111,21 @@ export default class MapType extends BasisType {
(this.value === undefined ||
type.key === undefined ||
(type.value instanceof Type &&
this.value.accepts(type.value, context)))
this.value.accepts(type.value, context))),
);
}

concretize(context: Context) {
return MapType.make(
this.key?.concretize(context),
this.value?.concretize(context),
);
}

generalize(context: Context) {
return MapType.make(
this.key?.generalize(context),
this.value?.generalize(context)
this.value?.generalize(context),
);
}

Expand All @@ -133,10 +140,10 @@ export default class MapType extends BasisType {
this.key instanceof Type
? this.key
: mapDef.types !== undefined &&
mapDef.types.variables[1].hasName(name) &&
this.value instanceof Type
? this.value
: undefined;
mapDef.types.variables[1].hasName(name) &&
this.value instanceof Type
? this.value
: undefined;
}

getNodeLocale(locales: Locales) {
Expand Down
15 changes: 11 additions & 4 deletions src/nodes/NameType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { UnknownName } from '@conflicts/UnknownName';
import Emotion from '../lore/Emotion';
import Sym from './Sym';
import type Locales from '../locale/Locales';
import StructureType from './StructureType';

export default class NameType extends Type {
readonly name: Token;
Expand All @@ -28,7 +29,7 @@ export default class NameType extends Type {
constructor(
type: Token | string,
types?: TypeInputs,
definition?: Definition
definition?: Definition,
) {
super();

Expand Down Expand Up @@ -57,7 +58,7 @@ export default class NameType extends Type {
clone(replace?: Replacement) {
return new NameType(
this.replaceChild('name', this.name, replace),
this.replaceChild('types', this.types, replace)
this.replaceChild('types', this.types, replace),
) as this;
}

Expand Down Expand Up @@ -105,8 +106,8 @@ export default class NameType extends Type {
new UnexpectedTypeInput(
this,
this.types.types[index],
def
)
def,
),
);
break;
}
Expand All @@ -127,6 +128,12 @@ export default class NameType extends Type {
return [this.getType(context)];
}

concretize(context: Context): Type {
const concrete = this.getType(context);
// If it's a structure type, return it, otherwise leave it as a type variable.
return concrete instanceof StructureType ? concrete : this;
}

resolve(context?: Context): Definition | undefined {
// Find the name in the binding scope.
return (
Expand Down
10 changes: 7 additions & 3 deletions src/nodes/SetType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default class SetType extends BasisType {
static getPossibleNodes(
type: Type | undefined,
node: Node,
selected: boolean
selected: boolean,
) {
return [
SetType.make(),
Expand All @@ -61,7 +61,7 @@ export default class SetType extends BasisType {
return new SetType(
this.replaceChild('open', this.open, replace),
this.replaceChild('key', this.key, replace),
this.replaceChild('close', this.close, replace)
this.replaceChild('close', this.close, replace),
) as this;
}

Expand All @@ -83,10 +83,14 @@ export default class SetType extends BasisType {
// If it is a specific type, see if the other set's type is unspecified or compatible
type.key === undefined ||
(type.key instanceof Type &&
this.key.accepts(type.key, context)))
this.key.accepts(type.key, context))),
);
}

concretize(context: Context): Type {
return SetType.make(this.key?.concretize(context));
}

generalize(context: Context) {
return SetType.make(this.key?.generalize(context));
}
Expand Down
10 changes: 7 additions & 3 deletions src/nodes/StreamType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default class StreamType extends Type {
static make(type?: Type) {
return new StreamType(
new Token(STREAM_SYMBOL, Sym.Stream),
type ?? new AnyType()
type ?? new AnyType(),
);
}

Expand All @@ -53,18 +53,22 @@ export default class StreamType extends Type {
.every(
(type) =>
type instanceof StreamType &&
this.type.accepts(type.type, context)
this.type.accepts(type.type, context),
);
}

getBasisTypeName(): BasisTypeName {
return 'stream';
}

concretize(context: Context) {
return StreamType.make(this.type.concretize(context));
}

clone(replace?: Replacement) {
return new StreamType(
this.replaceChild('stream', this.stream, replace),
this.replaceChild('type', this.type, replace)
this.replaceChild('type', this.type, replace),
) as this;
}

Expand Down
Loading

0 comments on commit 8f23b8c

Please sign in to comment.