From 7ce61c39ab55ccdbb2a808eedc1910076b13f103 Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Mon, 16 Dec 2024 14:41:41 -0500 Subject: [PATCH 1/6] Export `makeUnelaboratedObjectNode` from semantics module --- .../semantics/expressions/apply-expression.ts | 6 ++++-- .../semantics/expressions/check-expression.ts | 10 +++++----- .../semantics/expressions/expression-utilities.ts | 10 +++++----- .../semantics/expressions/function-expression.ts | 6 ++---- .../semantics/expressions/runtime-expression.ts | 2 +- src/language/compiling/semantics/prelude.ts | 6 ++---- src/language/semantics.ts | 1 + src/language/semantics/expression-elaboration.ts | 8 ++++++-- 8 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/language/compiling/semantics/expressions/apply-expression.ts b/src/language/compiling/semantics/expressions/apply-expression.ts index bd87fee..bdd7914 100644 --- a/src/language/compiling/semantics/expressions/apply-expression.ts +++ b/src/language/compiling/semantics/expressions/apply-expression.ts @@ -1,14 +1,16 @@ import { either, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' import type { Molecule } from '../../../parsing.js' -import { isFunctionNode } from '../../../semantics.js' +import { + isFunctionNode, + makeUnelaboratedObjectNode, +} from '../../../semantics.js' import { isExpression, type Expression, type ExpressionContext, type KeywordHandler, } from '../../../semantics/expression-elaboration.js' -import { makeUnelaboratedObjectNode } from '../../../semantics/object-node.js' import { containsAnyUnelaboratedNodes, type SemanticGraph, diff --git a/src/language/compiling/semantics/expressions/check-expression.ts b/src/language/compiling/semantics/expressions/check-expression.ts index d5f8464..0e67884 100644 --- a/src/language/compiling/semantics/expressions/check-expression.ts +++ b/src/language/compiling/semantics/expressions/check-expression.ts @@ -1,17 +1,17 @@ import { either, option, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' import type { Molecule } from '../../../parsing.js' -import { isFunctionNode } from '../../../semantics.js' +import { + isFunctionNode, + makeUnelaboratedObjectNode, +} from '../../../semantics.js' import { isExpression, type Expression, type ExpressionContext, type KeywordHandler, } from '../../../semantics/expression-elaboration.js' -import { - lookupPropertyOfObjectNode, - makeUnelaboratedObjectNode, -} from '../../../semantics/object-node.js' +import { lookupPropertyOfObjectNode } from '../../../semantics/object-node.js' import { stringifySemanticGraphForEndUser, type SemanticGraph, diff --git a/src/language/compiling/semantics/expressions/expression-utilities.ts b/src/language/compiling/semantics/expressions/expression-utilities.ts index 3b86749..e854988 100644 --- a/src/language/compiling/semantics/expressions/expression-utilities.ts +++ b/src/language/compiling/semantics/expressions/expression-utilities.ts @@ -1,16 +1,16 @@ import { either, option, type Either, type Option } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' import type { Atom, Molecule } from '../../../parsing.js' -import { type ObjectNode } from '../../../semantics.js' +import { + makeUnelaboratedObjectNode, + type ObjectNode, +} from '../../../semantics.js' import type { Expression, ExpressionContext, } from '../../../semantics/expression-elaboration.js' import { stringifyKeyPathForEndUser } from '../../../semantics/key-path.js' -import { - lookupPropertyOfObjectNode, - makeUnelaboratedObjectNode, -} from '../../../semantics/object-node.js' +import { lookupPropertyOfObjectNode } from '../../../semantics/object-node.js' import { applyKeyPathToSemanticGraph, isSemanticGraph, diff --git a/src/language/compiling/semantics/expressions/function-expression.ts b/src/language/compiling/semantics/expressions/function-expression.ts index 8a50962..46f6646 100644 --- a/src/language/compiling/semantics/expressions/function-expression.ts +++ b/src/language/compiling/semantics/expressions/function-expression.ts @@ -3,6 +3,7 @@ import type { ElaborationError } from '../../../errors.js' import type { Atom, Molecule } from '../../../parsing.js' import { makeFunctionNode, + makeUnelaboratedObjectNode, serialize, types, type FunctionNode, @@ -14,10 +15,7 @@ import { type ExpressionContext, type KeywordHandler, } from '../../../semantics/expression-elaboration.js' -import { - makeObjectNode, - makeUnelaboratedObjectNode, -} from '../../../semantics/object-node.js' +import { makeObjectNode } from '../../../semantics/object-node.js' import { updateValueAtKeyPathInSemanticGraph, type SemanticGraph, diff --git a/src/language/compiling/semantics/expressions/runtime-expression.ts b/src/language/compiling/semantics/expressions/runtime-expression.ts index f26045e..d40fe62 100644 --- a/src/language/compiling/semantics/expressions/runtime-expression.ts +++ b/src/language/compiling/semantics/expressions/runtime-expression.ts @@ -3,6 +3,7 @@ import type { ElaborationError } from '../../../errors.js' import { isAssignable, isFunctionNode, + makeUnelaboratedObjectNode, replaceAllTypeParametersWithTheirConstraints, types, } from '../../../semantics.js' @@ -12,7 +13,6 @@ import { type ExpressionContext, type KeywordHandler, } from '../../../semantics/expression-elaboration.js' -import { makeUnelaboratedObjectNode } from '../../../semantics/object-node.js' import { containsAnyUnelaboratedNodes, type SemanticGraph, diff --git a/src/language/compiling/semantics/prelude.ts b/src/language/compiling/semantics/prelude.ts index bf954d4..2d0b92c 100644 --- a/src/language/compiling/semantics/prelude.ts +++ b/src/language/compiling/semantics/prelude.ts @@ -6,14 +6,12 @@ import { isObjectNode, makeFunctionNode, makeObjectNode, + makeUnelaboratedObjectNode, types, type ObjectNode, } from '../../semantics.js' import { keyPathToMolecule } from '../../semantics/key-path.js' -import { - lookupPropertyOfObjectNode, - makeUnelaboratedObjectNode, -} from '../../semantics/object-node.js' +import { lookupPropertyOfObjectNode } from '../../semantics/object-node.js' import { containsAnyUnelaboratedNodes, isSemanticGraph, diff --git a/src/language/semantics.ts b/src/language/semantics.ts index 751ebb1..f350573 100644 --- a/src/language/semantics.ts +++ b/src/language/semantics.ts @@ -14,6 +14,7 @@ export { type KeyPath } from './semantics/key-path.js' export { isObjectNode, makeObjectNode, + makeUnelaboratedObjectNode, type ObjectNode, } from './semantics/object-node.js' export { diff --git a/src/language/semantics/expression-elaboration.ts b/src/language/semantics/expression-elaboration.ts index 39a93bf..51afb0f 100644 --- a/src/language/semantics/expression-elaboration.ts +++ b/src/language/semantics/expression-elaboration.ts @@ -3,8 +3,12 @@ import { withPhantomData, type WithPhantomData } from '../../phantom-data.js' import type { Writable } from '../../utility-types.js' import type { ElaborationError, InvalidSyntaxTreeError } from '../errors.js' import type { Atom, Molecule, SyntaxTree } from '../parsing.js' -import { makeObjectNode, type KeyPath, type ObjectNode } from '../semantics.js' -import { makeUnelaboratedObjectNode } from './object-node.js' +import { + makeObjectNode, + makeUnelaboratedObjectNode, + type KeyPath, + type ObjectNode, +} from '../semantics.js' import { extractStringValueIfPossible, updateValueAtKeyPathInSemanticGraph, From 14e90312535e423b944f8e0d056356b9b6cb9c80 Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Mon, 16 Dec 2024 14:51:10 -0500 Subject: [PATCH 2/6] Move `Expression` to its own module --- .../compiling/semantics/expressions/apply-expression.ts | 4 ++-- .../compiling/semantics/expressions/check-expression.ts | 4 ++-- .../semantics/expressions/expression-utilities.ts | 6 ++---- .../semantics/expressions/function-expression.ts | 4 ++-- .../compiling/semantics/expressions/lookup-expression.ts | 5 ++++- .../compiling/semantics/expressions/runtime-expression.ts | 4 ++-- .../compiling/semantics/expressions/todo-expression.ts | 2 +- src/language/runtime/keywords.ts | 2 +- src/language/semantics.ts | 1 + src/language/semantics/expression-elaboration.ts | 8 +------- src/language/semantics/expression.ts | 8 ++++++++ 11 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 src/language/semantics/expression.ts diff --git a/src/language/compiling/semantics/expressions/apply-expression.ts b/src/language/compiling/semantics/expressions/apply-expression.ts index bdd7914..e8a4200 100644 --- a/src/language/compiling/semantics/expressions/apply-expression.ts +++ b/src/language/compiling/semantics/expressions/apply-expression.ts @@ -2,12 +2,12 @@ import { either, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' import type { Molecule } from '../../../parsing.js' import { + isExpression, isFunctionNode, makeUnelaboratedObjectNode, + type Expression, } from '../../../semantics.js' import { - isExpression, - type Expression, type ExpressionContext, type KeywordHandler, } from '../../../semantics/expression-elaboration.js' diff --git a/src/language/compiling/semantics/expressions/check-expression.ts b/src/language/compiling/semantics/expressions/check-expression.ts index 0e67884..8e6c460 100644 --- a/src/language/compiling/semantics/expressions/check-expression.ts +++ b/src/language/compiling/semantics/expressions/check-expression.ts @@ -2,12 +2,12 @@ import { either, option, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' import type { Molecule } from '../../../parsing.js' import { + isExpression, isFunctionNode, makeUnelaboratedObjectNode, + type Expression, } from '../../../semantics.js' import { - isExpression, - type Expression, type ExpressionContext, type KeywordHandler, } from '../../../semantics/expression-elaboration.js' diff --git a/src/language/compiling/semantics/expressions/expression-utilities.ts b/src/language/compiling/semantics/expressions/expression-utilities.ts index e854988..e279f1f 100644 --- a/src/language/compiling/semantics/expressions/expression-utilities.ts +++ b/src/language/compiling/semantics/expressions/expression-utilities.ts @@ -3,12 +3,10 @@ import type { ElaborationError } from '../../../errors.js' import type { Atom, Molecule } from '../../../parsing.js' import { makeUnelaboratedObjectNode, + type Expression, type ObjectNode, } from '../../../semantics.js' -import type { - Expression, - ExpressionContext, -} from '../../../semantics/expression-elaboration.js' +import type { ExpressionContext } from '../../../semantics/expression-elaboration.js' import { stringifyKeyPathForEndUser } from '../../../semantics/key-path.js' import { lookupPropertyOfObjectNode } from '../../../semantics/object-node.js' import { diff --git a/src/language/compiling/semantics/expressions/function-expression.ts b/src/language/compiling/semantics/expressions/function-expression.ts index 46f6646..a7592cb 100644 --- a/src/language/compiling/semantics/expressions/function-expression.ts +++ b/src/language/compiling/semantics/expressions/function-expression.ts @@ -2,16 +2,16 @@ import { either, option, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' import type { Atom, Molecule } from '../../../parsing.js' import { + isExpression, makeFunctionNode, makeUnelaboratedObjectNode, serialize, types, + type Expression, type FunctionNode, } from '../../../semantics.js' import { elaborateWithContext, - isExpression, - type Expression, type ExpressionContext, type KeywordHandler, } from '../../../semantics/expression-elaboration.js' diff --git a/src/language/compiling/semantics/expressions/lookup-expression.ts b/src/language/compiling/semantics/expressions/lookup-expression.ts index d0bc12f..b844d54 100644 --- a/src/language/compiling/semantics/expressions/lookup-expression.ts +++ b/src/language/compiling/semantics/expressions/lookup-expression.ts @@ -3,10 +3,13 @@ import type { ElaborationError, InvalidExpressionError, } from '../../../errors.js' -import { isFunctionNode, type KeyPath } from '../../../semantics.js' import { isExpression, + isFunctionNode, type Expression, + type KeyPath, +} from '../../../semantics.js' +import { type ExpressionContext, type KeywordHandler, } from '../../../semantics/expression-elaboration.js' diff --git a/src/language/compiling/semantics/expressions/runtime-expression.ts b/src/language/compiling/semantics/expressions/runtime-expression.ts index d40fe62..d410cf4 100644 --- a/src/language/compiling/semantics/expressions/runtime-expression.ts +++ b/src/language/compiling/semantics/expressions/runtime-expression.ts @@ -2,14 +2,14 @@ import { either, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' import { isAssignable, + isExpression, isFunctionNode, makeUnelaboratedObjectNode, replaceAllTypeParametersWithTheirConstraints, types, + type Expression, } from '../../../semantics.js' import { - isExpression, - type Expression, type ExpressionContext, type KeywordHandler, } from '../../../semantics/expression-elaboration.js' diff --git a/src/language/compiling/semantics/expressions/todo-expression.ts b/src/language/compiling/semantics/expressions/todo-expression.ts index a09b051..702bd2c 100644 --- a/src/language/compiling/semantics/expressions/todo-expression.ts +++ b/src/language/compiling/semantics/expressions/todo-expression.ts @@ -1,7 +1,7 @@ import { either, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' +import type { Expression } from '../../../semantics.js' import { - type Expression, type ExpressionContext, type KeywordHandler, } from '../../../semantics/expression-elaboration.js' diff --git a/src/language/runtime/keywords.ts b/src/language/runtime/keywords.ts index b21c72d..59e68f4 100644 --- a/src/language/runtime/keywords.ts +++ b/src/language/runtime/keywords.ts @@ -9,11 +9,11 @@ import { makeObjectNode, serialize, types, + type Expression, type KeywordElaborationResult, type KeywordModule, type SemanticGraph, } from '../semantics.js' -import type { Expression } from '../semantics/expression-elaboration.js' import { lookupPropertyOfObjectNode } from '../semantics/object-node.js' const unserializableFunction = () => diff --git a/src/language/semantics.ts b/src/language/semantics.ts index f350573..0d6522d 100644 --- a/src/language/semantics.ts +++ b/src/language/semantics.ts @@ -5,6 +5,7 @@ export { type KeywordElaborationResult, type KeywordModule, } from './semantics/expression-elaboration.js' +export { isExpression, type Expression } from './semantics/expression.js' export { isFunctionNode, makeFunctionNode, diff --git a/src/language/semantics/expression-elaboration.ts b/src/language/semantics/expression-elaboration.ts index 51afb0f..046b55c 100644 --- a/src/language/semantics/expression-elaboration.ts +++ b/src/language/semantics/expression-elaboration.ts @@ -9,6 +9,7 @@ import { type KeyPath, type ObjectNode, } from '../semantics.js' +import type { Expression } from './expression.js' import { extractStringValueIfPossible, updateValueAtKeyPathInSemanticGraph, @@ -19,13 +20,6 @@ declare const _elaborated: unique symbol type Elaborated = { readonly [_elaborated]: true } export type ElaboratedSemanticGraph = WithPhantomData -export type Expression = ObjectNode & { - readonly 0: `@${string}` -} - -export const isExpression = (node: SemanticGraph): node is Expression => - typeof node === 'object' && typeof node[0] === 'string' && node[0][0] === '@' - export type ExpressionContext = { readonly program: SemanticGraph readonly location: KeyPath diff --git a/src/language/semantics/expression.ts b/src/language/semantics/expression.ts new file mode 100644 index 0000000..3698123 --- /dev/null +++ b/src/language/semantics/expression.ts @@ -0,0 +1,8 @@ +import type { ObjectNode, SemanticGraph } from '../semantics.js' + +export type Expression = ObjectNode & { + readonly 0: `@${string}` +} + +export const isExpression = (node: SemanticGraph): node is Expression => + typeof node === 'object' && typeof node[0] === 'string' && node[0][0] === '@' From 8c0e359522d375a16a896574bc01430bc098a636 Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Mon, 16 Dec 2024 22:48:52 -0500 Subject: [PATCH 3/6] Statically fix the set of keywords --- src/language/compiling.ts | 2 +- src/language/compiling/compiler.ts | 4 +- src/language/compiling/semantics.test.ts | 4 +- .../expressions/function-expression.ts | 6 +- src/language/compiling/semantics/keywords.ts | 13 +--- src/language/runtime/evaluator.ts | 4 +- src/language/runtime/keywords.ts | 16 ++--- src/language/semantics.ts | 3 +- .../semantics/expression-elaboration.ts | 67 +++++++++---------- src/language/semantics/keyword.ts | 13 ++++ 10 files changed, 64 insertions(+), 68 deletions(-) create mode 100644 src/language/semantics/keyword.ts diff --git a/src/language/compiling.ts b/src/language/compiling.ts index 4c5e90c..0ad810f 100644 --- a/src/language/compiling.ts +++ b/src/language/compiling.ts @@ -1,2 +1,2 @@ export { compile } from './compiling/compiler.js' -export { handlers as keywordHandlers } from './compiling/semantics/keywords.js' +export { keywordHandlers } from './compiling/semantics/keywords.js' diff --git a/src/language/compiling/compiler.ts b/src/language/compiling/compiler.ts index 4414b3a..f6ec925 100644 --- a/src/language/compiling/compiler.ts +++ b/src/language/compiling/compiler.ts @@ -3,12 +3,12 @@ import type { CompilationError } from '../errors.js' import type { JSONValueForbiddingSymbolicKeys } from '../parsing.js' import { canonicalize } from '../parsing.js' import { elaborate, serialize, type Output } from '../semantics.js' -import * as keywordModule from './semantics/keywords.js' +import { keywordHandlers } from './semantics/keywords.js' export const compile = ( input: JSONValueForbiddingSymbolicKeys, ): Either => { const syntaxTree = canonicalize(input) - const semanticGraphResult = elaborate(syntaxTree, keywordModule) + const semanticGraphResult = elaborate(syntaxTree, keywordHandlers) return either.flatMap(semanticGraphResult, serialize) } diff --git a/src/language/compiling/semantics.test.ts b/src/language/compiling/semantics.test.ts index 7bf5ba0..33231bc 100644 --- a/src/language/compiling/semantics.test.ts +++ b/src/language/compiling/semantics.test.ts @@ -13,12 +13,12 @@ import { type ObjectNode, } from '../semantics.js' import type { SemanticGraph } from '../semantics/semantic-graph.js' -import * as keywordModule from './semantics/keywords.js' +import { keywordHandlers } from './semantics/keywords.js' import { prelude } from './semantics/prelude.js' const elaborationSuite = testCases( (input: Atom | Molecule) => - elaborate(withPhantomData()(input), keywordModule), + elaborate(withPhantomData()(input), keywordHandlers), input => `elaborating expressions in \`${JSON.stringify(input)}\``, ) diff --git a/src/language/compiling/semantics/expressions/function-expression.ts b/src/language/compiling/semantics/expressions/function-expression.ts index a7592cb..0421c28 100644 --- a/src/language/compiling/semantics/expressions/function-expression.ts +++ b/src/language/compiling/semantics/expressions/function-expression.ts @@ -21,7 +21,7 @@ import { type SemanticGraph, type unelaboratedKey, } from '../../../semantics/semantic-graph.js' -import { handlers, isKeyword } from '../keywords.js' +import { keywordHandlers } from '../keywords.js' import { asSemanticGraph, readArgumentsFromExpression, @@ -115,7 +115,9 @@ const apply = ( updatedProgram => elaborateWithContext( serializedBody, - { handlers, isKeyword }, + // TODO: This should use compile-time or runtime handlers when appropriate. Perhaps + // keyword handlers should be part of `ExpressionContext`? + keywordHandlers, { location: [...context.location, returnKey], program: updatedProgram, diff --git a/src/language/compiling/semantics/keywords.ts b/src/language/compiling/semantics/keywords.ts index fa891e3..a4b1eab 100644 --- a/src/language/compiling/semantics/keywords.ts +++ b/src/language/compiling/semantics/keywords.ts @@ -1,4 +1,4 @@ -import { type KeywordModule } from '../../semantics.js' +import { type KeywordHandlers } from '../../semantics.js' import { applyKeyword, applyKeywordHandler, @@ -24,7 +24,7 @@ import { todoKeywordHandler, } from './expressions/todo-expression.js' -export const handlers = { +export const keywordHandlers: KeywordHandlers = { /** * Calls the given function with a given argument. */ @@ -54,11 +54,4 @@ export const handlers = { * Ignores all properties and evaluates to an empty object. */ [todoKeyword]: todoKeywordHandler, -} satisfies KeywordModule<`@${string}`>['handlers'] - -export type Keyword = keyof typeof handlers - -// `isKeyword` is correct as long as `handlers` does not have excess properties. -const allKeywords = new Set(Object.keys(handlers)) -export const isKeyword = (input: string): input is Keyword => - allKeywords.has(input) +} diff --git a/src/language/runtime/evaluator.ts b/src/language/runtime/evaluator.ts index 1f51382..945534e 100644 --- a/src/language/runtime/evaluator.ts +++ b/src/language/runtime/evaluator.ts @@ -3,12 +3,12 @@ import type { RuntimeError } from '../errors.js' import type { JSONValueForbiddingSymbolicKeys } from '../parsing.js' import { canonicalize } from '../parsing.js' import { elaborate, serialize, type Output } from '../semantics.js' -import * as keywordModule from './keywords.js' +import { keywordHandlers } from './keywords.js' export const evaluate = ( input: JSONValueForbiddingSymbolicKeys, ): Either => { const syntaxTree = canonicalize(input) - const semanticGraphResult = elaborate(syntaxTree, keywordModule) + const semanticGraphResult = elaborate(syntaxTree, keywordHandlers) return either.flatMap(semanticGraphResult, serialize) } diff --git a/src/language/runtime/keywords.ts b/src/language/runtime/keywords.ts index 59e68f4..c56e99a 100644 --- a/src/language/runtime/keywords.ts +++ b/src/language/runtime/keywords.ts @@ -10,8 +10,7 @@ import { serialize, types, type Expression, - type KeywordElaborationResult, - type KeywordModule, + type KeywordHandlers, type SemanticGraph, } from '../semantics.js' import { lookupPropertyOfObjectNode } from '../semantics/object-node.js' @@ -125,12 +124,12 @@ const runtimeContext = makeObjectNode({ }), }) -export const handlers = { +export const keywordHandlers: KeywordHandlers = { ...compilerKeywordHandlers, /** * Evaluates the given function, passing runtime context captured in `world`. */ - '@runtime': (expression): KeywordElaborationResult => { + '@runtime': expression => { const runtimeFunction = lookupWithinExpression( ['function', '1'], expression, @@ -158,14 +157,7 @@ export const handlers = { } } }, -} satisfies KeywordModule<`@${string}`>['handlers'] - -export type Keyword = keyof typeof handlers - -// `isKeyword` is correct as long as `handlers` does not have excess properties. -const allKeywords = new Set(Object.keys(handlers)) -export const isKeyword = (input: string): input is Keyword => - allKeywords.has(input) +} const lookupWithinExpression = ( keyAliases: [Atom, ...(readonly Atom[])], diff --git a/src/language/semantics.ts b/src/language/semantics.ts index 0d6522d..670275e 100644 --- a/src/language/semantics.ts +++ b/src/language/semantics.ts @@ -3,7 +3,7 @@ export { type ElaboratedSemanticGraph, type ExpressionContext, type KeywordElaborationResult, - type KeywordModule, + type KeywordHandlers, } from './semantics/expression-elaboration.js' export { isExpression, type Expression } from './semantics/expression.js' export { @@ -12,6 +12,7 @@ export { type FunctionNode, } from './semantics/function-node.js' export { type KeyPath } from './semantics/key-path.js' +export { isKeyword, type Keyword } from './semantics/keyword.js' export { isObjectNode, makeObjectNode, diff --git a/src/language/semantics/expression-elaboration.ts b/src/language/semantics/expression-elaboration.ts index 046b55c..bd4d301 100644 --- a/src/language/semantics/expression-elaboration.ts +++ b/src/language/semantics/expression-elaboration.ts @@ -3,13 +3,14 @@ import { withPhantomData, type WithPhantomData } from '../../phantom-data.js' import type { Writable } from '../../utility-types.js' import type { ElaborationError, InvalidSyntaxTreeError } from '../errors.js' import type { Atom, Molecule, SyntaxTree } from '../parsing.js' +import type { Expression } from './expression.js' +import type { KeyPath } from './key-path.js' +import { isKeyword, type Keyword } from './keyword.js' import { makeObjectNode, makeUnelaboratedObjectNode, - type KeyPath, type ObjectNode, -} from '../semantics.js' -import type { Expression } from './expression.js' +} from './object-node.js' import { extractStringValueIfPossible, updateValueAtKeyPathInSemanticGraph, @@ -32,16 +33,13 @@ export type KeywordHandler = ( context: ExpressionContext, ) => KeywordElaborationResult -export type KeywordModule = { - readonly isKeyword: (input: string) => input is Keyword - readonly handlers: Readonly> -} +export type KeywordHandlers = Readonly> export const elaborate = ( program: SyntaxTree, - keywordModule: KeywordModule<`@${string}`>, + keywordHandlers: KeywordHandlers, ): Either => - elaborateWithContext(program, keywordModule, { + elaborateWithContext(program, keywordHandlers, { location: [], program: typeof program === 'string' @@ -51,19 +49,19 @@ export const elaborate = ( export const elaborateWithContext = ( program: SyntaxTree, - keywordModule: KeywordModule<`@${string}`>, + keywordHandlers: KeywordHandlers, context: ExpressionContext, ): Either => either.map( typeof program === 'string' ? handleAtomWhichMayNotBeAKeyword(program) - : elaborateWithinMolecule(program, keywordModule, context), + : elaborateWithinMolecule(program, keywordHandlers, context), withPhantomData(), ) const elaborateWithinMolecule = ( molecule: Molecule, - keywordModule: KeywordModule<`@${string}`>, + keywordHandlers: KeywordHandlers, context: ExpressionContext, ): Either => { const possibleExpressionAsObjectNode: Writable = makeObjectNode( @@ -83,7 +81,7 @@ const elaborateWithinMolecule = ( } else { const elaborationResult = elaborateWithinMolecule( value, - keywordModule, + keywordHandlers, { location: [...context.location, key], program: updatedProgram, @@ -148,7 +146,7 @@ const elaborateWithinMolecule = ( ...possibleExpressionAsObjectNode, 0: possibleKeywordAsString, }, - keywordModule, + keywordHandlers, { program: updatedProgram, location: context.location, @@ -158,32 +156,29 @@ const elaborateWithinMolecule = ( } } -const handleObjectNodeWhichMayBeAExpression = ( +const handleObjectNodeWhichMayBeAExpression = ( node: ObjectNode & { readonly 0: Atom }, - keywordModule: KeywordModule, + keywordHandlers: KeywordHandlers, context: ExpressionContext, ): Either => { const { 0: possibleKeyword, ...possibleArguments } = node - return option.match( - option.fromPredicate(possibleKeyword, keywordModule.isKeyword), - { - none: () => - /^@[^@]/.test(possibleKeyword) - ? either.makeLeft({ - kind: 'unknownKeyword', - message: `unknown keyword: \`${possibleKeyword}\``, - }) - : either.makeRight({ - ...node, - 0: unescapeKeywordSigil(possibleKeyword), - }), - some: keyword => - keywordModule.handlers[keyword]( - makeObjectNode({ ...possibleArguments, 0: keyword }), - context, - ), - }, - ) + return option.match(option.fromPredicate(possibleKeyword, isKeyword), { + none: () => + /^@[^@]/.test(possibleKeyword) + ? either.makeLeft({ + kind: 'unknownKeyword', + message: `unknown keyword: \`${possibleKeyword}\``, + }) + : either.makeRight({ + ...node, + 0: unescapeKeywordSigil(possibleKeyword), + }), + some: keyword => + keywordHandlers[keyword]( + makeObjectNode({ ...possibleArguments, 0: keyword }), + context, + ), + }) } const handleAtomWhichMayNotBeAKeyword = ( diff --git a/src/language/semantics/keyword.ts b/src/language/semantics/keyword.ts new file mode 100644 index 0000000..f92ef17 --- /dev/null +++ b/src/language/semantics/keyword.ts @@ -0,0 +1,13 @@ +export const isKeyword = (input: string) => + input === '@apply' || + input === '@check' || + input === '@function' || + input === '@lookup' || + input === '@runtime' || + input === '@todo' + +export type Keyword = typeof isKeyword extends ( + input: string, +) => input is string & infer Keyword + ? Keyword + : never From f0f0441e79c459ca4bce93988285bd34dbdbd066 Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Mon, 16 Dec 2024 23:24:28 -0500 Subject: [PATCH 4/6] Move some expression stuff out of the compiler --- .../expressions/runtime-expression.ts | 91 ------------------- .../apply-handler.ts} | 47 +--------- .../check-handler.ts} | 47 +--------- .../function-handler.ts} | 53 +---------- .../lookup-handler.ts} | 58 +----------- .../keyword-handlers/runtime-handler.ts | 42 +++++++++ .../todo-handler.ts} | 6 -- src/language/compiling/semantics/keywords.ts | 42 +++------ src/language/semantics.ts | 30 ++++++ .../semantics/expressions/apply-expression.ts | 42 +++++++++ .../semantics/expressions/check-expression.ts | 42 +++++++++ .../expressions/expression-utilities.ts | 18 ++-- .../expressions/function-expression.ts | 54 +++++++++++ .../expressions/lookup-expression.ts | 81 +++++++++++++++++ .../expressions/runtime-expression.ts | 54 +++++++++++ .../semantics/expressions/todo-expression.ts | 5 + 16 files changed, 381 insertions(+), 331 deletions(-) delete mode 100644 src/language/compiling/semantics/expressions/runtime-expression.ts rename src/language/compiling/semantics/{expressions/apply-expression.ts => keyword-handlers/apply-handler.ts} (61%) rename src/language/compiling/semantics/{expressions/check-expression.ts => keyword-handlers/check-handler.ts} (78%) rename src/language/compiling/semantics/{expressions/function-expression.ts => keyword-handlers/function-handler.ts} (63%) rename src/language/compiling/semantics/{expressions/lookup-expression.ts => keyword-handlers/lookup-handler.ts} (80%) create mode 100644 src/language/compiling/semantics/keyword-handlers/runtime-handler.ts rename src/language/compiling/semantics/{expressions/todo-expression.ts => keyword-handlers/todo-handler.ts} (85%) create mode 100644 src/language/semantics/expressions/apply-expression.ts create mode 100644 src/language/semantics/expressions/check-expression.ts rename src/language/{compiling => }/semantics/expressions/expression-utilities.ts (82%) create mode 100644 src/language/semantics/expressions/function-expression.ts create mode 100644 src/language/semantics/expressions/lookup-expression.ts create mode 100644 src/language/semantics/expressions/runtime-expression.ts create mode 100644 src/language/semantics/expressions/todo-expression.ts diff --git a/src/language/compiling/semantics/expressions/runtime-expression.ts b/src/language/compiling/semantics/expressions/runtime-expression.ts deleted file mode 100644 index d410cf4..0000000 --- a/src/language/compiling/semantics/expressions/runtime-expression.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { either, type Either } from '../../../../adts.js' -import type { ElaborationError } from '../../../errors.js' -import { - isAssignable, - isExpression, - isFunctionNode, - makeUnelaboratedObjectNode, - replaceAllTypeParametersWithTheirConstraints, - types, - type Expression, -} from '../../../semantics.js' -import { - type ExpressionContext, - type KeywordHandler, -} from '../../../semantics/expression-elaboration.js' -import { - containsAnyUnelaboratedNodes, - type SemanticGraph, - type unelaboratedKey, -} from '../../../semantics/semantic-graph.js' -import { - asSemanticGraph, - readArgumentsFromExpression, -} from './expression-utilities.js' - -export const runtimeKeyword = '@runtime' - -export type RuntimeExpression = Expression & { - readonly 0: '@runtime' - readonly function: SemanticGraph -} - -export const readRuntimeExpression = ( - node: SemanticGraph, -): Either => - isExpression(node) - ? either.flatMap( - readArgumentsFromExpression(node, [['function', '1']]), - ([f]) => { - const runtimeFunction = asSemanticGraph(f) - if ( - !( - isFunctionNode(runtimeFunction) || containsAnyUnelaboratedNodes(f) - ) - ) { - return either.makeLeft({ - kind: 'invalidExpression', - message: 'runtime functions must compute something', - }) - } else { - return either.makeRight(makeRuntimeExpression(runtimeFunction)) - } - }, - ) - : either.makeLeft({ - kind: 'invalidExpression', - message: 'not an expression', - }) - -export const makeRuntimeExpression = ( - f: SemanticGraph, -): RuntimeExpression & { readonly [unelaboratedKey]: true } => - makeUnelaboratedObjectNode({ - 0: '@runtime', - function: f, - }) - -export const runtimeKeywordHandler: KeywordHandler = ( - expression: Expression, - _context: ExpressionContext, -): Either => - either.flatMap(readRuntimeExpression(expression), ({ function: f }) => { - const runtimeFunction = asSemanticGraph(f) - if (isFunctionNode(runtimeFunction)) { - const runtimeFunctionSignature = runtimeFunction.signature - return !isAssignable({ - source: types.runtimeContext, - target: replaceAllTypeParametersWithTheirConstraints( - runtimeFunctionSignature.parameter, - ), - }) - ? either.makeLeft({ - kind: 'typeMismatch', - message: '@runtime function must accept a runtime context argument', - }) - : either.makeRight(makeRuntimeExpression(f)) - } else { - // TODO: Type-check unelaborated nodes. - return either.makeRight(makeRuntimeExpression(f)) - } - }) diff --git a/src/language/compiling/semantics/expressions/apply-expression.ts b/src/language/compiling/semantics/keyword-handlers/apply-handler.ts similarity index 61% rename from src/language/compiling/semantics/expressions/apply-expression.ts rename to src/language/compiling/semantics/keyword-handlers/apply-handler.ts index e8a4200..cae2428 100644 --- a/src/language/compiling/semantics/expressions/apply-expression.ts +++ b/src/language/compiling/semantics/keyword-handlers/apply-handler.ts @@ -1,10 +1,9 @@ import { either, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' -import type { Molecule } from '../../../parsing.js' import { - isExpression, + asSemanticGraph, isFunctionNode, - makeUnelaboratedObjectNode, + readApplyExpression, type Expression, } from '../../../semantics.js' import { @@ -14,49 +13,7 @@ import { import { containsAnyUnelaboratedNodes, type SemanticGraph, - type unelaboratedKey, } from '../../../semantics/semantic-graph.js' -import { - asSemanticGraph, - readArgumentsFromExpression, -} from './expression-utilities.js' - -export const applyKeyword = '@apply' - -export type ApplyExpression = Expression & { - readonly 0: '@apply' - readonly function: SemanticGraph | Molecule - readonly argument: SemanticGraph | Molecule -} - -export const readApplyExpression = ( - node: SemanticGraph, -): Either => - isExpression(node) - ? either.map( - readArgumentsFromExpression(node, [ - ['function', '1'], - ['argument', '2'], - ]), - ([f, argument]) => makeApplyExpression({ function: f, argument }), - ) - : either.makeLeft({ - kind: 'invalidExpression', - message: 'not an expression', - }) - -export const makeApplyExpression = ({ - function: f, - argument, -}: { - readonly function: SemanticGraph | Molecule - readonly argument: SemanticGraph | Molecule -}): ApplyExpression & { readonly [unelaboratedKey]: true } => - makeUnelaboratedObjectNode({ - 0: '@apply', - function: f, - argument, - }) export const applyKeywordHandler: KeywordHandler = ( expression: Expression, diff --git a/src/language/compiling/semantics/expressions/check-expression.ts b/src/language/compiling/semantics/keyword-handlers/check-handler.ts similarity index 78% rename from src/language/compiling/semantics/expressions/check-expression.ts rename to src/language/compiling/semantics/keyword-handlers/check-handler.ts index 8e6c460..a3b15b6 100644 --- a/src/language/compiling/semantics/expressions/check-expression.ts +++ b/src/language/compiling/semantics/keyword-handlers/check-handler.ts @@ -1,10 +1,9 @@ import { either, option, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' -import type { Molecule } from '../../../parsing.js' import { - isExpression, + asSemanticGraph, isFunctionNode, - makeUnelaboratedObjectNode, + readCheckExpression, type Expression, } from '../../../semantics.js' import { @@ -15,49 +14,7 @@ import { lookupPropertyOfObjectNode } from '../../../semantics/object-node.js' import { stringifySemanticGraphForEndUser, type SemanticGraph, - type unelaboratedKey, } from '../../../semantics/semantic-graph.js' -import { - asSemanticGraph, - readArgumentsFromExpression, -} from './expression-utilities.js' - -export const checkKeyword = '@check' - -export type CheckExpression = Expression & { - readonly 0: '@check' - readonly value: SemanticGraph | Molecule - readonly type: SemanticGraph | Molecule -} - -export const readCheckExpression = ( - node: SemanticGraph, -): Either => - isExpression(node) - ? either.map( - readArgumentsFromExpression(node, [ - ['value', '1'], - ['type', '2'], - ]), - ([value, type]) => makeCheckExpression({ value, type }), - ) - : either.makeLeft({ - kind: 'invalidExpression', - message: 'not an expression', - }) - -export const makeCheckExpression = ({ - value, - type, -}: { - value: SemanticGraph | Molecule - type: SemanticGraph | Molecule -}): CheckExpression & { readonly [unelaboratedKey]: true } => - makeUnelaboratedObjectNode({ - 0: '@check', - value, - type, - }) export const checkKeywordHandler: KeywordHandler = ( expression: Expression, diff --git a/src/language/compiling/semantics/expressions/function-expression.ts b/src/language/compiling/semantics/keyword-handlers/function-handler.ts similarity index 63% rename from src/language/compiling/semantics/expressions/function-expression.ts rename to src/language/compiling/semantics/keyword-handlers/function-handler.ts index 0421c28..f299ab9 100644 --- a/src/language/compiling/semantics/expressions/function-expression.ts +++ b/src/language/compiling/semantics/keyword-handlers/function-handler.ts @@ -1,13 +1,13 @@ import { either, option, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' -import type { Atom, Molecule } from '../../../parsing.js' import { - isExpression, + asSemanticGraph, makeFunctionNode, - makeUnelaboratedObjectNode, + readFunctionExpression, serialize, types, type Expression, + type FunctionExpression, type FunctionNode, } from '../../../semantics.js' import { @@ -19,55 +19,8 @@ import { makeObjectNode } from '../../../semantics/object-node.js' import { updateValueAtKeyPathInSemanticGraph, type SemanticGraph, - type unelaboratedKey, } from '../../../semantics/semantic-graph.js' import { keywordHandlers } from '../keywords.js' -import { - asSemanticGraph, - readArgumentsFromExpression, -} from './expression-utilities.js' - -export const functionKeyword = '@function' - -export type FunctionExpression = Expression & { - readonly 0: '@function' - readonly parameter: Atom - readonly body: SemanticGraph | Molecule -} - -export const readFunctionExpression = ( - node: SemanticGraph, -): Either => - isExpression(node) - ? either.flatMap( - readArgumentsFromExpression(node, [ - ['parameter', '1'], - ['body', '2'], - ]), - ([parameter, body]): Either => - typeof parameter !== 'string' - ? either.makeLeft({ - kind: 'invalidExpression', - message: 'parameter name must be an atom', - }) - : either.map(serialize(asSemanticGraph(body)), body => - makeFunctionExpression(parameter, body), - ), - ) - : either.makeLeft({ - kind: 'invalidExpression', - message: 'not an expression', - }) - -export const makeFunctionExpression = ( - parameter: Atom, - body: SemanticGraph | Molecule, -): FunctionExpression & { readonly [unelaboratedKey]: true } => - makeUnelaboratedObjectNode({ - 0: '@function', - parameter, - body, - }) export const functionKeywordHandler: KeywordHandler = ( expression: Expression, diff --git a/src/language/compiling/semantics/expressions/lookup-expression.ts b/src/language/compiling/semantics/keyword-handlers/lookup-handler.ts similarity index 80% rename from src/language/compiling/semantics/expressions/lookup-expression.ts rename to src/language/compiling/semantics/keyword-handlers/lookup-handler.ts index b844d54..2fc6e61 100644 --- a/src/language/compiling/semantics/expressions/lookup-expression.ts +++ b/src/language/compiling/semantics/keyword-handlers/lookup-handler.ts @@ -4,8 +4,9 @@ import type { InvalidExpressionError, } from '../../../errors.js' import { - isExpression, - isFunctionNode, + makeLookupExpression, + readFunctionExpression, + readLookupExpression, type Expression, type KeyPath, } from '../../../semantics.js' @@ -20,66 +21,13 @@ import { import { isObjectNode, makeObjectNode, - makeUnelaboratedObjectNode, type ObjectNode, } from '../../../semantics/object-node.js' import { applyKeyPathToSemanticGraph, type SemanticGraph, - type unelaboratedKey, } from '../../../semantics/semantic-graph.js' import { prelude } from '../prelude.js' -import { - asSemanticGraph, - readArgumentsFromExpression, -} from './expression-utilities.js' -import { readFunctionExpression } from './function-expression.js' - -export const lookupKeyword = '@lookup' - -export type LookupExpression = Expression & { - readonly 0: '@lookup' - readonly query: ObjectNode -} - -export const readLookupExpression = ( - node: SemanticGraph, -): Either => - isExpression(node) - ? either.flatMap( - readArgumentsFromExpression(node, [['query', '1']]), - ([q]) => { - const query = asSemanticGraph(q) - if (isFunctionNode(query)) { - return either.makeLeft({ - kind: 'invalidExpression', - message: 'query cannot be a function', - }) - } else { - const canonicalizedQuery = - typeof query === 'string' - ? makeObjectNode(keyPathToMolecule(query.split('.'))) - : query - - return either.map( - keyPathFromObjectNode(canonicalizedQuery), - _keyPath => makeLookupExpression(canonicalizedQuery), - ) - } - }, - ) - : either.makeLeft({ - kind: 'invalidExpression', - message: 'not an expression', - }) - -export const makeLookupExpression = ( - query: ObjectNode, -): LookupExpression & { readonly [unelaboratedKey]: true } => - makeUnelaboratedObjectNode({ - 0: '@lookup', - query, - }) export const lookupKeywordHandler: KeywordHandler = ( expression: Expression, diff --git a/src/language/compiling/semantics/keyword-handlers/runtime-handler.ts b/src/language/compiling/semantics/keyword-handlers/runtime-handler.ts new file mode 100644 index 0000000..7a40006 --- /dev/null +++ b/src/language/compiling/semantics/keyword-handlers/runtime-handler.ts @@ -0,0 +1,42 @@ +import { either, type Either } from '../../../../adts.js' +import type { ElaborationError } from '../../../errors.js' +import { + asSemanticGraph, + isAssignable, + isFunctionNode, + makeRuntimeExpression, + readRuntimeExpression, + replaceAllTypeParametersWithTheirConstraints, + types, + type Expression, +} from '../../../semantics.js' +import { + type ExpressionContext, + type KeywordHandler, +} from '../../../semantics/expression-elaboration.js' +import { type SemanticGraph } from '../../../semantics/semantic-graph.js' + +export const runtimeKeywordHandler: KeywordHandler = ( + expression: Expression, + _context: ExpressionContext, +): Either => + either.flatMap(readRuntimeExpression(expression), ({ function: f }) => { + const runtimeFunction = asSemanticGraph(f) + if (isFunctionNode(runtimeFunction)) { + const runtimeFunctionSignature = runtimeFunction.signature + return !isAssignable({ + source: types.runtimeContext, + target: replaceAllTypeParametersWithTheirConstraints( + runtimeFunctionSignature.parameter, + ), + }) + ? either.makeLeft({ + kind: 'typeMismatch', + message: '@runtime function must accept a runtime context argument', + }) + : either.makeRight(makeRuntimeExpression(f)) + } else { + // TODO: Type-check unelaborated nodes. + return either.makeRight(makeRuntimeExpression(f)) + } + }) diff --git a/src/language/compiling/semantics/expressions/todo-expression.ts b/src/language/compiling/semantics/keyword-handlers/todo-handler.ts similarity index 85% rename from src/language/compiling/semantics/expressions/todo-expression.ts rename to src/language/compiling/semantics/keyword-handlers/todo-handler.ts index 702bd2c..1fc66f2 100644 --- a/src/language/compiling/semantics/expressions/todo-expression.ts +++ b/src/language/compiling/semantics/keyword-handlers/todo-handler.ts @@ -8,12 +8,6 @@ import { import { makeObjectNode } from '../../../semantics/object-node.js' import { type SemanticGraph } from '../../../semantics/semantic-graph.js' -export const todoKeyword = '@todo' - -export type TodoExpression = Expression & { - readonly 0: '@todo' -} - export const todoKeywordHandler: KeywordHandler = ( _expression: Expression, _context: ExpressionContext, diff --git a/src/language/compiling/semantics/keywords.ts b/src/language/compiling/semantics/keywords.ts index a4b1eab..4efe461 100644 --- a/src/language/compiling/semantics/keywords.ts +++ b/src/language/compiling/semantics/keywords.ts @@ -1,57 +1,39 @@ import { type KeywordHandlers } from '../../semantics.js' -import { - applyKeyword, - applyKeywordHandler, -} from './expressions/apply-expression.js' -import { - checkKeyword, - checkKeywordHandler, -} from './expressions/check-expression.js' -import { - functionKeyword, - functionKeywordHandler, -} from './expressions/function-expression.js' -import { - lookupKeyword, - lookupKeywordHandler, -} from './expressions/lookup-expression.js' -import { - runtimeKeyword, - runtimeKeywordHandler, -} from './expressions/runtime-expression.js' -import { - todoKeyword, - todoKeywordHandler, -} from './expressions/todo-expression.js' +import { applyKeywordHandler } from './keyword-handlers/apply-handler.js' +import { checkKeywordHandler } from './keyword-handlers/check-handler.js' +import { functionKeywordHandler } from './keyword-handlers/function-handler.js' +import { lookupKeywordHandler } from './keyword-handlers/lookup-handler.js' +import { runtimeKeywordHandler } from './keyword-handlers/runtime-handler.js' +import { todoKeywordHandler } from './keyword-handlers/todo-handler.js' export const keywordHandlers: KeywordHandlers = { /** * Calls the given function with a given argument. */ - [applyKeyword]: applyKeywordHandler, + '@apply': applyKeywordHandler, /** * Checks whether a given value is assignable to a given type. */ - [checkKeyword]: checkKeywordHandler, + '@check': checkKeywordHandler, /** * Creates a function. */ - [functionKeyword]: functionKeywordHandler, + '@function': functionKeywordHandler, /** * Given a query, resolves the value of a property within the program. */ - [lookupKeyword]: lookupKeywordHandler, + '@lookup': lookupKeywordHandler, /** * Defers evaluation until runtime. */ - [runtimeKeyword]: runtimeKeywordHandler, + '@runtime': runtimeKeywordHandler, /** * Ignores all properties and evaluates to an empty object. */ - [todoKeyword]: todoKeywordHandler, + '@todo': todoKeywordHandler, } diff --git a/src/language/semantics.ts b/src/language/semantics.ts index 670275e..8ae5152 100644 --- a/src/language/semantics.ts +++ b/src/language/semantics.ts @@ -6,6 +6,36 @@ export { type KeywordHandlers, } from './semantics/expression-elaboration.js' export { isExpression, type Expression } from './semantics/expression.js' +export { + makeApplyExpression, + readApplyExpression, + type ApplyExpression, +} from './semantics/expressions/apply-expression.js' +export { + makeCheckExpression, + readCheckExpression, + type CheckExpression, +} from './semantics/expressions/check-expression.js' +export { + asSemanticGraph, + readArgumentsFromExpression, +} from './semantics/expressions/expression-utilities.js' +export { + makeFunctionExpression, + readFunctionExpression, + type FunctionExpression, +} from './semantics/expressions/function-expression.js' +export { + makeLookupExpression, + readLookupExpression, + type LookupExpression, +} from './semantics/expressions/lookup-expression.js' +export { + makeRuntimeExpression, + readRuntimeExpression, + type RuntimeExpression, +} from './semantics/expressions/runtime-expression.js' +export { type TodoExpression } from './semantics/expressions/todo-expression.js' export { isFunctionNode, makeFunctionNode, diff --git a/src/language/semantics/expressions/apply-expression.ts b/src/language/semantics/expressions/apply-expression.ts new file mode 100644 index 0000000..371a8cc --- /dev/null +++ b/src/language/semantics/expressions/apply-expression.ts @@ -0,0 +1,42 @@ +import { either, type Either } from '../../../adts.js' +import type { ElaborationError } from '../../errors.js' +import type { Molecule } from '../../parsing.js' +import { isExpression, type Expression } from '../expression.js' +import { makeUnelaboratedObjectNode } from '../object-node.js' +import { type SemanticGraph, type unelaboratedKey } from '../semantic-graph.js' +import { readArgumentsFromExpression } from './expression-utilities.js' + +export type ApplyExpression = Expression & { + readonly 0: '@apply' + readonly function: SemanticGraph | Molecule + readonly argument: SemanticGraph | Molecule +} + +export const readApplyExpression = ( + node: SemanticGraph, +): Either => + isExpression(node) + ? either.map( + readArgumentsFromExpression(node, [ + ['function', '1'], + ['argument', '2'], + ]), + ([f, argument]) => makeApplyExpression({ function: f, argument }), + ) + : either.makeLeft({ + kind: 'invalidExpression', + message: 'not an expression', + }) + +export const makeApplyExpression = ({ + function: f, + argument, +}: { + readonly function: SemanticGraph | Molecule + readonly argument: SemanticGraph | Molecule +}): ApplyExpression & { readonly [unelaboratedKey]: true } => + makeUnelaboratedObjectNode({ + 0: '@apply', + function: f, + argument, + }) diff --git a/src/language/semantics/expressions/check-expression.ts b/src/language/semantics/expressions/check-expression.ts new file mode 100644 index 0000000..351d3d7 --- /dev/null +++ b/src/language/semantics/expressions/check-expression.ts @@ -0,0 +1,42 @@ +import { either, type Either } from '../../../adts.js' +import type { ElaborationError } from '../../errors.js' +import type { Molecule } from '../../parsing.js' +import { isExpression, type Expression } from '../expression.js' +import { makeUnelaboratedObjectNode } from '../object-node.js' +import { type SemanticGraph, type unelaboratedKey } from '../semantic-graph.js' +import { readArgumentsFromExpression } from './expression-utilities.js' + +export type CheckExpression = Expression & { + readonly 0: '@check' + readonly value: SemanticGraph | Molecule + readonly type: SemanticGraph | Molecule +} + +export const readCheckExpression = ( + node: SemanticGraph, +): Either => + isExpression(node) + ? either.map( + readArgumentsFromExpression(node, [ + ['value', '1'], + ['type', '2'], + ]), + ([value, type]) => makeCheckExpression({ value, type }), + ) + : either.makeLeft({ + kind: 'invalidExpression', + message: 'not an expression', + }) + +export const makeCheckExpression = ({ + value, + type, +}: { + value: SemanticGraph | Molecule + type: SemanticGraph | Molecule +}): CheckExpression & { readonly [unelaboratedKey]: true } => + makeUnelaboratedObjectNode({ + 0: '@check', + value, + type, + }) diff --git a/src/language/compiling/semantics/expressions/expression-utilities.ts b/src/language/semantics/expressions/expression-utilities.ts similarity index 82% rename from src/language/compiling/semantics/expressions/expression-utilities.ts rename to src/language/semantics/expressions/expression-utilities.ts index e279f1f..b3c543a 100644 --- a/src/language/compiling/semantics/expressions/expression-utilities.ts +++ b/src/language/semantics/expressions/expression-utilities.ts @@ -1,19 +1,19 @@ -import { either, option, type Either, type Option } from '../../../../adts.js' -import type { ElaborationError } from '../../../errors.js' -import type { Atom, Molecule } from '../../../parsing.js' +import { either, option, type Either, type Option } from '../../../adts.js' +import type { ElaborationError } from '../../errors.js' +import type { Atom, Molecule } from '../../parsing.js' +import type { ExpressionContext } from '../expression-elaboration.js' +import type { Expression } from '../expression.js' +import { stringifyKeyPathForEndUser } from '../key-path.js' import { + lookupPropertyOfObjectNode, makeUnelaboratedObjectNode, - type Expression, type ObjectNode, -} from '../../../semantics.js' -import type { ExpressionContext } from '../../../semantics/expression-elaboration.js' -import { stringifyKeyPathForEndUser } from '../../../semantics/key-path.js' -import { lookupPropertyOfObjectNode } from '../../../semantics/object-node.js' +} from '../object-node.js' import { applyKeyPathToSemanticGraph, isSemanticGraph, type SemanticGraph, -} from '../../../semantics/semantic-graph.js' +} from '../semantic-graph.js' export const asSemanticGraph = ( value: SemanticGraph | Molecule, diff --git a/src/language/semantics/expressions/function-expression.ts b/src/language/semantics/expressions/function-expression.ts new file mode 100644 index 0000000..e37d5d3 --- /dev/null +++ b/src/language/semantics/expressions/function-expression.ts @@ -0,0 +1,54 @@ +import { either, type Either } from '../../../adts.js' +import type { ElaborationError } from '../../errors.js' +import type { Atom, Molecule } from '../../parsing.js' +import { isExpression, type Expression } from '../expression.js' +import { makeUnelaboratedObjectNode } from '../object-node.js' +import { + serialize, + type SemanticGraph, + type unelaboratedKey, +} from '../semantic-graph.js' +import { + asSemanticGraph, + readArgumentsFromExpression, +} from './expression-utilities.js' + +export type FunctionExpression = Expression & { + readonly 0: '@function' + readonly parameter: Atom + readonly body: SemanticGraph | Molecule +} + +export const readFunctionExpression = ( + node: SemanticGraph, +): Either => + isExpression(node) + ? either.flatMap( + readArgumentsFromExpression(node, [ + ['parameter', '1'], + ['body', '2'], + ]), + ([parameter, body]): Either => + typeof parameter !== 'string' + ? either.makeLeft({ + kind: 'invalidExpression', + message: 'parameter name must be an atom', + }) + : either.map(serialize(asSemanticGraph(body)), body => + makeFunctionExpression(parameter, body), + ), + ) + : either.makeLeft({ + kind: 'invalidExpression', + message: 'not an expression', + }) + +export const makeFunctionExpression = ( + parameter: Atom, + body: SemanticGraph | Molecule, +): FunctionExpression & { readonly [unelaboratedKey]: true } => + makeUnelaboratedObjectNode({ + 0: '@function', + parameter, + body, + }) diff --git a/src/language/semantics/expressions/lookup-expression.ts b/src/language/semantics/expressions/lookup-expression.ts new file mode 100644 index 0000000..0271e26 --- /dev/null +++ b/src/language/semantics/expressions/lookup-expression.ts @@ -0,0 +1,81 @@ +import { either, type Either } from '../../../adts.js' +import type { ElaborationError, InvalidExpressionError } from '../../errors.js' +import { isExpression, type Expression } from '../expression.js' +import { isFunctionNode } from '../function-node.js' +import { keyPathToMolecule, type KeyPath } from '../key-path.js' +import { + makeObjectNode, + makeUnelaboratedObjectNode, + type ObjectNode, +} from '../object-node.js' +import { type SemanticGraph, type unelaboratedKey } from '../semantic-graph.js' +import { + asSemanticGraph, + readArgumentsFromExpression, +} from './expression-utilities.js' + +export type LookupExpression = Expression & { + readonly 0: '@lookup' + readonly query: ObjectNode +} + +export const readLookupExpression = ( + node: SemanticGraph, +): Either => + isExpression(node) + ? either.flatMap( + readArgumentsFromExpression(node, [['query', '1']]), + ([q]) => { + const query = asSemanticGraph(q) + if (isFunctionNode(query)) { + return either.makeLeft({ + kind: 'invalidExpression', + message: 'query cannot be a function', + }) + } else { + const canonicalizedQuery = + typeof query === 'string' + ? makeObjectNode(keyPathToMolecule(query.split('.'))) + : query + + return either.map( + keyPathFromObjectNode(canonicalizedQuery), + _keyPath => makeLookupExpression(canonicalizedQuery), + ) + } + }, + ) + : either.makeLeft({ + kind: 'invalidExpression', + message: 'not an expression', + }) + +export const makeLookupExpression = ( + query: ObjectNode, +): LookupExpression & { readonly [unelaboratedKey]: true } => + makeUnelaboratedObjectNode({ + 0: '@lookup', + query, + }) + +const keyPathFromObjectNode = ( + node: ObjectNode, +): Either => { + const relativePath: string[] = [] + let queryIndex = 0 + // Consume numeric indexes ("0", "1", …) until exhausted, validating that each is an atom. + let key = node[queryIndex] + while (key !== undefined) { + if (typeof key !== 'string') { + return either.makeLeft({ + kind: 'invalidExpression', + message: 'query must be a key path composed of sequential atoms', + }) + } else { + relativePath.push(key) + } + queryIndex++ + key = node[queryIndex] + } + return either.makeRight(relativePath) +} diff --git a/src/language/semantics/expressions/runtime-expression.ts b/src/language/semantics/expressions/runtime-expression.ts new file mode 100644 index 0000000..1ccaa60 --- /dev/null +++ b/src/language/semantics/expressions/runtime-expression.ts @@ -0,0 +1,54 @@ +import { either, type Either } from '../../../adts.js' +import type { ElaborationError } from '../../errors.js' +import { isExpression, type Expression } from '../expression.js' +import { isFunctionNode } from '../function-node.js' +import { makeUnelaboratedObjectNode } from '../object-node.js' +import { + containsAnyUnelaboratedNodes, + type SemanticGraph, + type unelaboratedKey, +} from '../semantic-graph.js' +import { + asSemanticGraph, + readArgumentsFromExpression, +} from './expression-utilities.js' + +export type RuntimeExpression = Expression & { + readonly 0: '@runtime' + readonly function: SemanticGraph +} + +export const readRuntimeExpression = ( + node: SemanticGraph, +): Either => + isExpression(node) + ? either.flatMap( + readArgumentsFromExpression(node, [['function', '1']]), + ([f]) => { + const runtimeFunction = asSemanticGraph(f) + if ( + !( + isFunctionNode(runtimeFunction) || containsAnyUnelaboratedNodes(f) + ) + ) { + return either.makeLeft({ + kind: 'invalidExpression', + message: 'runtime functions must compute something', + }) + } else { + return either.makeRight(makeRuntimeExpression(runtimeFunction)) + } + }, + ) + : either.makeLeft({ + kind: 'invalidExpression', + message: 'not an expression', + }) + +export const makeRuntimeExpression = ( + f: SemanticGraph, +): RuntimeExpression & { readonly [unelaboratedKey]: true } => + makeUnelaboratedObjectNode({ + 0: '@runtime', + function: f, + }) diff --git a/src/language/semantics/expressions/todo-expression.ts b/src/language/semantics/expressions/todo-expression.ts new file mode 100644 index 0000000..111cb3c --- /dev/null +++ b/src/language/semantics/expressions/todo-expression.ts @@ -0,0 +1,5 @@ +import { type Expression } from '../expression.js' + +export type TodoExpression = Expression & { + readonly 0: '@todo' +} From 379802ed92ebc4cb1668fdc7ede35a5e6e57fca5 Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Mon, 16 Dec 2024 23:30:25 -0500 Subject: [PATCH 5/6] Put keyword handlers in expression context --- .../keyword-handlers/function-handler.ts | 16 ++++------- .../keyword-handlers/lookup-handler.ts | 6 +++- .../semantics/expression-elaboration.ts | 28 ++++++++----------- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/language/compiling/semantics/keyword-handlers/function-handler.ts b/src/language/compiling/semantics/keyword-handlers/function-handler.ts index f299ab9..ac5f06b 100644 --- a/src/language/compiling/semantics/keyword-handlers/function-handler.ts +++ b/src/language/compiling/semantics/keyword-handlers/function-handler.ts @@ -20,7 +20,6 @@ import { updateValueAtKeyPathInSemanticGraph, type SemanticGraph, } from '../../../semantics/semantic-graph.js' -import { keywordHandlers } from '../keywords.js' export const functionKeywordHandler: KeywordHandler = ( expression: Expression, @@ -66,16 +65,11 @@ const apply = ( }), ), updatedProgram => - elaborateWithContext( - serializedBody, - // TODO: This should use compile-time or runtime handlers when appropriate. Perhaps - // keyword handlers should be part of `ExpressionContext`? - keywordHandlers, - { - location: [...context.location, returnKey], - program: updatedProgram, - }, - ), + elaborateWithContext(serializedBody, { + keywordHandlers: context.keywordHandlers, + location: [...context.location, returnKey], + program: updatedProgram, + }), ), ) diff --git a/src/language/compiling/semantics/keyword-handlers/lookup-handler.ts b/src/language/compiling/semantics/keyword-handlers/lookup-handler.ts index 2fc6e61..b289056 100644 --- a/src/language/compiling/semantics/keyword-handlers/lookup-handler.ts +++ b/src/language/compiling/semantics/keyword-handlers/lookup-handler.ts @@ -181,7 +181,11 @@ const lookup = ({ // Try the parent scope. lookup({ relativePath, - context: { program: context.program, location: pathToCurrentScope }, + context: { + keywordHandlers: context.keywordHandlers, + location: pathToCurrentScope, + program: context.program, + }, }), some: lookedUpValue => either.makeRight(option.makeSome(lookedUpValue)), }), diff --git a/src/language/semantics/expression-elaboration.ts b/src/language/semantics/expression-elaboration.ts index bd4d301..dd23586 100644 --- a/src/language/semantics/expression-elaboration.ts +++ b/src/language/semantics/expression-elaboration.ts @@ -22,8 +22,9 @@ type Elaborated = { readonly [_elaborated]: true } export type ElaboratedSemanticGraph = WithPhantomData export type ExpressionContext = { - readonly program: SemanticGraph + readonly keywordHandlers: KeywordHandlers readonly location: KeyPath + readonly program: SemanticGraph } export type KeywordElaborationResult = Either @@ -39,7 +40,8 @@ export const elaborate = ( program: SyntaxTree, keywordHandlers: KeywordHandlers, ): Either => - elaborateWithContext(program, keywordHandlers, { + elaborateWithContext(program, { + keywordHandlers, location: [], program: typeof program === 'string' @@ -49,19 +51,17 @@ export const elaborate = ( export const elaborateWithContext = ( program: SyntaxTree, - keywordHandlers: KeywordHandlers, context: ExpressionContext, ): Either => either.map( typeof program === 'string' ? handleAtomWhichMayNotBeAKeyword(program) - : elaborateWithinMolecule(program, keywordHandlers, context), + : elaborateWithinMolecule(program, context), withPhantomData(), ) const elaborateWithinMolecule = ( molecule: Molecule, - keywordHandlers: KeywordHandlers, context: ExpressionContext, ): Either => { const possibleExpressionAsObjectNode: Writable = makeObjectNode( @@ -79,14 +79,11 @@ const elaborateWithinMolecule = ( if (typeof value === 'string') { possibleExpressionAsObjectNode[updatedKey] = value } else { - const elaborationResult = elaborateWithinMolecule( - value, - keywordHandlers, - { - location: [...context.location, key], - program: updatedProgram, - }, - ) + const elaborationResult = elaborateWithinMolecule(value, { + keywordHandlers: context.keywordHandlers, + location: [...context.location, key], + program: updatedProgram, + }) if (either.isLeft(elaborationResult)) { // Immediately bail on error. return elaborationResult @@ -146,8 +143,8 @@ const elaborateWithinMolecule = ( ...possibleExpressionAsObjectNode, 0: possibleKeywordAsString, }, - keywordHandlers, { + keywordHandlers: context.keywordHandlers, program: updatedProgram, location: context.location, }, @@ -158,7 +155,6 @@ const elaborateWithinMolecule = ( const handleObjectNodeWhichMayBeAExpression = ( node: ObjectNode & { readonly 0: Atom }, - keywordHandlers: KeywordHandlers, context: ExpressionContext, ): Either => { const { 0: possibleKeyword, ...possibleArguments } = node @@ -174,7 +170,7 @@ const handleObjectNodeWhichMayBeAExpression = ( 0: unescapeKeywordSigil(possibleKeyword), }), some: keyword => - keywordHandlers[keyword]( + context.keywordHandlers[keyword]( makeObjectNode({ ...possibleArguments, 0: keyword }), context, ), From 3660504f2c10cffa0229e4fd8257f9a3979246f4 Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Mon, 16 Dec 2024 23:54:06 -0500 Subject: [PATCH 6/6] Move prelude out of compiler, improve many imports --- src/language/compiling/semantics.test.ts | 2 +- .../keyword-handlers/apply-handler.ts | 8 ++----- .../keyword-handlers/check-handler.ts | 10 +++----- .../keyword-handlers/function-handler.ts | 14 ++++------- .../keyword-handlers/lookup-handler.ts | 24 +++++++------------ .../keyword-handlers/runtime-handler.ts | 6 ++--- .../keyword-handlers/todo-handler.ts | 8 +++---- src/language/semantics.ts | 14 ++++++++++- .../{compiling => }/semantics/prelude.ts | 21 ++++++++-------- 9 files changed, 48 insertions(+), 59 deletions(-) rename src/language/{compiling => }/semantics/prelude.ts (96%) diff --git a/src/language/compiling/semantics.test.ts b/src/language/compiling/semantics.test.ts index 33231bc..3db73b3 100644 --- a/src/language/compiling/semantics.test.ts +++ b/src/language/compiling/semantics.test.ts @@ -12,9 +12,9 @@ import { type ElaboratedSemanticGraph, type ObjectNode, } from '../semantics.js' +import { prelude } from '../semantics/prelude.js' import type { SemanticGraph } from '../semantics/semantic-graph.js' import { keywordHandlers } from './semantics/keywords.js' -import { prelude } from './semantics/prelude.js' const elaborationSuite = testCases( (input: Atom | Molecule) => diff --git a/src/language/compiling/semantics/keyword-handlers/apply-handler.ts b/src/language/compiling/semantics/keyword-handlers/apply-handler.ts index cae2428..555abc4 100644 --- a/src/language/compiling/semantics/keyword-handlers/apply-handler.ts +++ b/src/language/compiling/semantics/keyword-handlers/apply-handler.ts @@ -2,18 +2,14 @@ import { either, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' import { asSemanticGraph, + containsAnyUnelaboratedNodes, isFunctionNode, readApplyExpression, type Expression, -} from '../../../semantics.js' -import { type ExpressionContext, type KeywordHandler, -} from '../../../semantics/expression-elaboration.js' -import { - containsAnyUnelaboratedNodes, type SemanticGraph, -} from '../../../semantics/semantic-graph.js' +} from '../../../semantics.js' export const applyKeywordHandler: KeywordHandler = ( expression: Expression, diff --git a/src/language/compiling/semantics/keyword-handlers/check-handler.ts b/src/language/compiling/semantics/keyword-handlers/check-handler.ts index a3b15b6..556a86b 100644 --- a/src/language/compiling/semantics/keyword-handlers/check-handler.ts +++ b/src/language/compiling/semantics/keyword-handlers/check-handler.ts @@ -3,18 +3,14 @@ import type { ElaborationError } from '../../../errors.js' import { asSemanticGraph, isFunctionNode, + lookupPropertyOfObjectNode, readCheckExpression, + stringifySemanticGraphForEndUser, type Expression, -} from '../../../semantics.js' -import { type ExpressionContext, type KeywordHandler, -} from '../../../semantics/expression-elaboration.js' -import { lookupPropertyOfObjectNode } from '../../../semantics/object-node.js' -import { - stringifySemanticGraphForEndUser, type SemanticGraph, -} from '../../../semantics/semantic-graph.js' +} from '../../../semantics.js' export const checkKeywordHandler: KeywordHandler = ( expression: Expression, diff --git a/src/language/compiling/semantics/keyword-handlers/function-handler.ts b/src/language/compiling/semantics/keyword-handlers/function-handler.ts index ac5f06b..e8679f7 100644 --- a/src/language/compiling/semantics/keyword-handlers/function-handler.ts +++ b/src/language/compiling/semantics/keyword-handlers/function-handler.ts @@ -2,24 +2,20 @@ import { either, option, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' import { asSemanticGraph, + elaborateWithContext, makeFunctionNode, + makeObjectNode, readFunctionExpression, serialize, types, + updateValueAtKeyPathInSemanticGraph, type Expression, + type ExpressionContext, type FunctionExpression, type FunctionNode, -} from '../../../semantics.js' -import { - elaborateWithContext, - type ExpressionContext, type KeywordHandler, -} from '../../../semantics/expression-elaboration.js' -import { makeObjectNode } from '../../../semantics/object-node.js' -import { - updateValueAtKeyPathInSemanticGraph, type SemanticGraph, -} from '../../../semantics/semantic-graph.js' +} from '../../../semantics.js' export const functionKeywordHandler: KeywordHandler = ( expression: Expression, diff --git a/src/language/compiling/semantics/keyword-handlers/lookup-handler.ts b/src/language/compiling/semantics/keyword-handlers/lookup-handler.ts index b289056..6cb9366 100644 --- a/src/language/compiling/semantics/keyword-handlers/lookup-handler.ts +++ b/src/language/compiling/semantics/keyword-handlers/lookup-handler.ts @@ -4,30 +4,22 @@ import type { InvalidExpressionError, } from '../../../errors.js' import { + applyKeyPathToSemanticGraph, + isObjectNode, + keyPathToMolecule, makeLookupExpression, + makeObjectNode, + prelude, readFunctionExpression, readLookupExpression, + stringifyKeyPathForEndUser, type Expression, - type KeyPath, -} from '../../../semantics.js' -import { type ExpressionContext, + type KeyPath, type KeywordHandler, -} from '../../../semantics/expression-elaboration.js' -import { - keyPathToMolecule, - stringifyKeyPathForEndUser, -} from '../../../semantics/key-path.js' -import { - isObjectNode, - makeObjectNode, type ObjectNode, -} from '../../../semantics/object-node.js' -import { - applyKeyPathToSemanticGraph, type SemanticGraph, -} from '../../../semantics/semantic-graph.js' -import { prelude } from '../prelude.js' +} from '../../../semantics.js' export const lookupKeywordHandler: KeywordHandler = ( expression: Expression, diff --git a/src/language/compiling/semantics/keyword-handlers/runtime-handler.ts b/src/language/compiling/semantics/keyword-handlers/runtime-handler.ts index 7a40006..ec0ca1d 100644 --- a/src/language/compiling/semantics/keyword-handlers/runtime-handler.ts +++ b/src/language/compiling/semantics/keyword-handlers/runtime-handler.ts @@ -9,12 +9,10 @@ import { replaceAllTypeParametersWithTheirConstraints, types, type Expression, -} from '../../../semantics.js' -import { type ExpressionContext, type KeywordHandler, -} from '../../../semantics/expression-elaboration.js' -import { type SemanticGraph } from '../../../semantics/semantic-graph.js' + type SemanticGraph, +} from '../../../semantics.js' export const runtimeKeywordHandler: KeywordHandler = ( expression: Expression, diff --git a/src/language/compiling/semantics/keyword-handlers/todo-handler.ts b/src/language/compiling/semantics/keyword-handlers/todo-handler.ts index 1fc66f2..96ff8ba 100644 --- a/src/language/compiling/semantics/keyword-handlers/todo-handler.ts +++ b/src/language/compiling/semantics/keyword-handlers/todo-handler.ts @@ -1,12 +1,12 @@ import { either, type Either } from '../../../../adts.js' import type { ElaborationError } from '../../../errors.js' -import type { Expression } from '../../../semantics.js' import { + makeObjectNode, + type Expression, type ExpressionContext, type KeywordHandler, -} from '../../../semantics/expression-elaboration.js' -import { makeObjectNode } from '../../../semantics/object-node.js' -import { type SemanticGraph } from '../../../semantics/semantic-graph.js' + type SemanticGraph, +} from '../../../semantics.js' export const todoKeywordHandler: KeywordHandler = ( _expression: Expression, diff --git a/src/language/semantics.ts b/src/language/semantics.ts index 8ae5152..4793e91 100644 --- a/src/language/semantics.ts +++ b/src/language/semantics.ts @@ -1,8 +1,10 @@ export { elaborate, + elaborateWithContext, type ElaboratedSemanticGraph, type ExpressionContext, type KeywordElaborationResult, + type KeywordHandler, type KeywordHandlers, } from './semantics/expression-elaboration.js' export { isExpression, type Expression } from './semantics/expression.js' @@ -41,16 +43,26 @@ export { makeFunctionNode, type FunctionNode, } from './semantics/function-node.js' -export { type KeyPath } from './semantics/key-path.js' +export { + keyPathToMolecule, + stringifyKeyPathForEndUser, + type KeyPath, +} from './semantics/key-path.js' export { isKeyword, type Keyword } from './semantics/keyword.js' export { isObjectNode, + lookupPropertyOfObjectNode, makeObjectNode, makeUnelaboratedObjectNode, type ObjectNode, } from './semantics/object-node.js' +export { prelude } from './semantics/prelude.js' export { + applyKeyPathToSemanticGraph, + containsAnyUnelaboratedNodes, serialize, + stringifySemanticGraphForEndUser, + updateValueAtKeyPathInSemanticGraph, type Output, type SemanticGraph, } from './semantics/semantic-graph.js' diff --git a/src/language/compiling/semantics/prelude.ts b/src/language/semantics/prelude.ts similarity index 96% rename from src/language/compiling/semantics/prelude.ts rename to src/language/semantics/prelude.ts index 2d0b92c..9340343 100644 --- a/src/language/compiling/semantics/prelude.ts +++ b/src/language/semantics/prelude.ts @@ -1,29 +1,28 @@ -import { either, option, type Either } from '../../../adts.js' -import type { DependencyUnavailable, Panic } from '../../errors.js' -import type { Atom } from '../../parsing.js' +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 { - isFunctionNode, isObjectNode, - makeFunctionNode, + lookupPropertyOfObjectNode, makeObjectNode, makeUnelaboratedObjectNode, - types, type ObjectNode, -} from '../../semantics.js' -import { keyPathToMolecule } from '../../semantics/key-path.js' -import { lookupPropertyOfObjectNode } from '../../semantics/object-node.js' +} from './object-node.js' import { containsAnyUnelaboratedNodes, isSemanticGraph, type SemanticGraph, -} from '../../semantics/semantic-graph.js' +} from './semantic-graph.js' +import { types } from './type-system.js' import { makeFunctionType, makeObjectType, makeTypeParameter, makeUnionType, type FunctionType, -} from '../../semantics/type-system/type-formats.js' +} from './type-system/type-formats.js' const handleUnavailableDependencies = (