Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -143,22 +143,14 @@ const lookup = ({
),
right: functionExpression => {
if (functionExpression.parameter === firstPathComponent) {
if (!relativePath.every(key => typeof key === 'string')) {
return either.makeLeft({
kind: 'invalidExpression',
message:
'dynamically-resolved lookup query contains symbolic key',
})
} else {
// Keep an unelaborated `@lookup` around for resolution when the `@function` is called.
return either.makeRight(
option.makeSome(
makeLookupExpression(
makeObjectNode(keyPathToMolecule(relativePath)),
),
// Keep an unelaborated `@lookup` around for resolution when the `@function` is called.
return either.makeRight(
option.makeSome(
makeLookupExpression(
makeObjectNode(keyPathToMolecule(relativePath)),
),
)
}
),
)
} else {
return either.makeRight(option.none)
}
Expand Down
3 changes: 0 additions & 3 deletions src/language/parsing/syntax-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ export const applyKeyPathToSyntaxTree = (
} else {
if (typeof syntaxTree === 'string') {
return option.none
} else if (typeof firstKey === 'symbol') {
// TODO: Treat this as an error? Or change the type of `keyPath`?
return option.none
} else {
const next = withPhantomData<Canonicalized>()(syntaxTree[firstKey])
if (next === undefined) {
Expand Down
20 changes: 2 additions & 18 deletions src/language/semantics/key-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,7 @@ import type { Atom, Molecule } from '../parsing.js'
import { unparse } from '../unparsing.js'
import { prettyPlz } from '../unparsing/pretty-plz.js'

export const functionParameter = Symbol('functionParameter')
export const functionReturn = Symbol('functionReturn')
export const typeParameterAssignableToConstraint = Symbol(
'typeParameterAssignableToConstraint',
)
export type KeyPath = readonly (
| Atom
// These symbol keys are somewhat "internal" at the moment. If they end up not being expressible
// in the surface syntax then `KeyPath` should be split into two separate types.
| typeof functionParameter
| typeof functionReturn
| typeof typeParameterAssignableToConstraint
)[]
export type KeyPath = readonly Atom[]

export const stringifyKeyPathForEndUser = (keyPath: KeyPath): string =>
either.match(
Expand All @@ -28,8 +16,4 @@ export const stringifyKeyPathForEndUser = (keyPath: KeyPath): string =>
)

export const keyPathToMolecule = (keyPath: KeyPath): Molecule =>
Object.fromEntries(
keyPath.flatMap((key, index) =>
typeof key === 'symbol' ? [] : [[index, key]],
),
)
Object.fromEntries(keyPath.flatMap((key, index) => [[index, key]]))
6 changes: 3 additions & 3 deletions src/language/semantics/prelude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { either, option, type Either } from '../../adts.js'
import type { DependencyUnavailable, Panic } from '../errors.js'
import type { Atom } from '../parsing.js'
import { isFunctionNode, makeFunctionNode } from './function-node.js'
import { keyPathToMolecule } from './key-path.js'
import { keyPathToMolecule, type KeyPath } from './key-path.js'
import {
isObjectNode,
lookupPropertyOfObjectNode,
Expand Down Expand Up @@ -44,7 +44,7 @@ const handleUnavailableDependencies =
}

const serializePartiallyAppliedFunction =
(keyPath: readonly string[], argument: SemanticGraph) => () =>
(keyPath: KeyPath, argument: SemanticGraph) => () =>
either.makeRight(
makeUnelaboratedObjectNode({
0: '@apply',
Expand All @@ -54,7 +54,7 @@ const serializePartiallyAppliedFunction =
)

const preludeFunction = (
keyPath: readonly string[],
keyPath: KeyPath,
signature: FunctionType['signature'],
f: (
value: SemanticGraph,
Expand Down
52 changes: 22 additions & 30 deletions src/language/semantics/semantic-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,14 @@ export const applyKeyPathToSemanticGraph = (
atom: _ => option.none,
function: _ => option.none,
object: graph => {
if (typeof firstKey === 'symbol') {
const next = graph[firstKey]
if (next === undefined) {
return option.none
} else {
const next = graph[firstKey]
if (next === undefined) {
return option.none
} else {
return applyKeyPathToSemanticGraph(
isSemanticGraph(next) ? next : syntaxTreeToSemanticGraph(next),
remainingKeyPath,
)
}
return applyKeyPathToSemanticGraph(
isSemanticGraph(next) ? next : syntaxTreeToSemanticGraph(next),
remainingKeyPath,
)
}
},
})
Expand Down Expand Up @@ -106,28 +102,24 @@ export const updateValueAtKeyPathInSemanticGraph = (
atom: _ => either.makeLeft(makePropertyNotFoundError(keyPath)),
function: _ => either.makeLeft(makePropertyNotFoundError(keyPath)),
object: node => {
if (typeof firstKey === 'symbol') {
const next = node[firstKey]
if (next === undefined) {
return either.makeLeft(makePropertyNotFoundError(keyPath))
} else {
const next = node[firstKey]
if (next === undefined) {
return either.makeLeft(makePropertyNotFoundError(keyPath))
} else {
return either.map(
updateValueAtKeyPathInSemanticGraph(
isSemanticGraph(next) ? next : syntaxTreeToSemanticGraph(next),
remainingKeyPath,
operation,
),
updatedNode =>
(isUnelaborated(node)
? makeUnelaboratedObjectNode
: makeObjectNode)({
...node,
[firstKey]: updatedNode,
}),
)
}
return either.map(
updateValueAtKeyPathInSemanticGraph(
isSemanticGraph(next) ? next : syntaxTreeToSemanticGraph(next),
remainingKeyPath,
operation,
),
updatedNode =>
(isUnelaborated(node)
? makeUnelaboratedObjectNode
: makeObjectNode)({
...node,
[firstKey]: updatedNode,
}),
)
}
},
})
Expand Down
36 changes: 22 additions & 14 deletions src/language/semantics/type-system/type-utilities.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import type { Writable } from '../../../utility-types.js'
import {
functionParameter,
functionReturn,
typeParameterAssignableToConstraint,
type KeyPath,
} from '../key-path.js'
import type { Atom } from '../../parsing.js'
import { types } from '../type-system.js'
import { simplifyUnionType } from './subtyping.js'
import {
Expand All @@ -19,14 +14,27 @@ import {
type UnionType,
} from './type-formats.js'

const functionParameter = Symbol('functionParameter')
const functionReturn = Symbol('functionReturn')
const typeParameterAssignableToConstraint = Symbol(
'typeParameterAssignableToConstraint',
)

export type TypeKeyPath = readonly (
| Atom
| typeof functionParameter
| typeof functionReturn
| typeof typeParameterAssignableToConstraint
)[]

type StringifiedKeyPath = string // this could be branded if that seems useful
type UnionOfTypeParameters = Omit<UnionType, 'members'> & {
readonly members: ReadonlySet<TypeParameter>
}
export type TypeParametersByKeyPath = Map<
StringifiedKeyPath,
{
readonly keyPath: KeyPath
readonly keyPath: TypeKeyPath
readonly typeParameters: UnionOfTypeParameters
}
>
Expand All @@ -36,7 +44,7 @@ export const containedTypeParameters = (type: Type): TypeParametersByKeyPath =>

const containedTypeParametersImplementation = (
type: Type,
root: KeyPath,
root: TypeKeyPath,
): TypeParametersByKeyPath => {
// Avoid infinite recursion when we hit the top type.
if (type === types.something) {
Expand Down Expand Up @@ -96,14 +104,14 @@ const containedTypeParametersImplementation = (
export const findKeyPathsToTypeParameter = (
type: Type,
typeParameterToFind: TypeParameter,
): Set<KeyPath> =>
): Set<TypeKeyPath> =>
findKeyPathsToTypeParameterImplementation(type, typeParameterToFind, [])

const findKeyPathsToTypeParameterImplementation = (
type: Type,
typeParameterToFind: TypeParameter,
root: KeyPath,
): Set<KeyPath> => {
root: TypeKeyPath,
): Set<TypeKeyPath> => {
// Avoid infinite recursion when we hit the top type.
if (type === types.something) {
return new Set()
Expand Down Expand Up @@ -148,7 +156,7 @@ const findKeyPathsToTypeParameterImplementation = (
union: ({ members }) =>
[...members]
.map(
(member): Set<KeyPath> =>
(member): Set<TypeKeyPath> =>
typeof member === 'string'
? new Set()
: findKeyPathsToTypeParameterImplementation(
Expand Down Expand Up @@ -253,7 +261,7 @@ export const supplyTypeArgument = (
*/
export const updateTypeAtKeyPathIfValid = (
type: Type,
keyPath: KeyPath,
keyPath: TypeKeyPath,
// TODO: `operation` should be able to update `Atom`s
operation: (typeAtKeyPath: Exclude<Type, UnionType>) => Type,
): Type => {
Expand Down Expand Up @@ -400,7 +408,7 @@ const mergeTypeParametersByKeyPath = (

// The string format is not meant for human consumption. The only guarantee is that every distinct
// key path produces a unique string.
const stringifyKeyPath = (keyPath: KeyPath): string =>
const stringifyKeyPath = (keyPath: TypeKeyPath): string =>
keyPath.reduce((stringifiedKeyPath: string, key) => {
const stringifiedKey =
typeof key === 'symbol' ? key.description : JSON.stringify(key)
Expand Down
4 changes: 1 addition & 3 deletions src/language/unparsing/pretty-plz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,7 @@ const sugaredLookup = (keyPathAsNode: Molecule | SemanticGraph) => {
if (
keyPath !== 'invalid' &&
Object.keys(keyPathAsNode).length === keyPath.length &&
keyPath.every(
key => typeof key === 'string' && !either.isLeft(unquotedAtomParser(key)),
)
keyPath.every(key => !either.isLeft(unquotedAtomParser(key)))
) {
return either.makeRight(kleur.cyan(colon.concat(keyPath.join(dot))))
} else {
Expand Down
Loading