Skip to content

Commit 239bab8

Browse files
committed
Formalize expressions a bit more
1 parent f207650 commit 239bab8

File tree

3 files changed

+45
-26
lines changed

3 files changed

+45
-26
lines changed

src/language/compiling/semantics/keywords.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ import {
1515
type KeyPath,
1616
type KeywordElaborationResult,
1717
type KeywordModule,
18-
type ObjectNode,
1918
type SemanticGraph,
2019
} from '../../semantics.js'
21-
import { elaborateWithContext } from '../../semantics/expression-elaboration.js'
20+
import {
21+
elaborateWithContext,
22+
isExpression,
23+
type Expression,
24+
} from '../../semantics/expression-elaboration.js'
2225
import { stringifyKeyPathForEndUser } from '../../semantics/key-path.js'
2326
import {
2427
lookupPropertyOfObjectNode,
@@ -237,8 +240,11 @@ export const handlers = {
237240
* Calls the given `FunctionNode` with a given argument.
238241
*/
239242
'@apply': (expression, _context): KeywordElaborationResult => {
240-
const functionToApply = lookupWithinArgument(['function', '1'], expression)
241-
const argument = lookupWithinArgument(['argument', '2'], expression)
243+
const functionToApply = lookupWithinExpression(
244+
['function', '1'],
245+
expression,
246+
)
247+
const argument = lookupWithinExpression(['argument', '2'], expression)
242248

243249
if (option.isNone(functionToApply)) {
244250
return either.makeLeft({
@@ -300,8 +306,8 @@ export const handlers = {
300306
* Checks whether a given value is assignable to a given type.
301307
*/
302308
'@check': (expression, context): KeywordElaborationResult => {
303-
const value = lookupWithinArgument(['value', '1'], expression)
304-
const type = lookupWithinArgument(['type', '2'], expression)
309+
const value = lookupWithinExpression(['value', '1'], expression)
310+
const type = lookupWithinExpression(['type', '2'], expression)
305311
if (option.isNone(value)) {
306312
return either.makeLeft({
307313
kind: 'invalidExpression',
@@ -331,7 +337,7 @@ export const handlers = {
331337
* Given a query, resolves the value of a property within the program.
332338
*/
333339
'@lookup': (expression, context): KeywordElaborationResult => {
334-
const query = lookupWithinArgument(['query', '1'], expression)
340+
const query = lookupWithinExpression(['query', '1'], expression)
335341
if (option.isNone(query)) {
336342
return either.makeLeft({
337343
kind: 'invalidExpression',
@@ -405,7 +411,10 @@ export const handlers = {
405411
* Preserves a raw function node for emission into the runtime code.
406412
*/
407413
'@runtime': (expression, context): KeywordElaborationResult => {
408-
const runtimeFunction = lookupWithinArgument(['function', '1'], expression)
414+
const runtimeFunction = lookupWithinExpression(
415+
['function', '1'],
416+
expression,
417+
)
409418
if (
410419
option.isNone(runtimeFunction) ||
411420
!(
@@ -498,7 +507,7 @@ export const applyLambda = (
498507
}
499508
}
500509

501-
type Lambda = ObjectNode & {
510+
type Lambda = Expression & {
502511
readonly 0: '@function'
503512
readonly parameter: Atom
504513
readonly body: SemanticGraph | Molecule
@@ -507,14 +516,14 @@ type Lambda = ObjectNode & {
507516
const validateLambda = (
508517
possibleLambda: SemanticGraph,
509518
): Either<ElaborationError, Lambda> => {
510-
if (typeof possibleLambda !== 'object') {
519+
if (!isExpression(possibleLambda)) {
511520
return either.makeLeft({
512521
kind: 'invalidExpression',
513-
message: 'functions must be encoded as objects',
522+
message: 'functions must be expressions',
514523
})
515524
} else {
516-
const parameter = lookupWithinArgument(['parameter', '1'], possibleLambda)
517-
const body = lookupWithinArgument(['body', '2'], possibleLambda)
525+
const parameter = lookupWithinExpression(['parameter', '1'], possibleLambda)
526+
const body = lookupWithinExpression(['body', '2'], possibleLambda)
518527

519528
if (option.isNone(parameter)) {
520529
return either.makeLeft({
@@ -546,7 +555,7 @@ const validateLambda = (
546555
}
547556

548557
const compileLambda = (
549-
possibleLambda: SemanticGraph,
558+
possibleLambda: Expression,
550559
context: ExpressionContext,
551560
): Either<ElaborationError, FunctionNode> => {
552561
if (typeof possibleLambda !== 'object') {
@@ -555,8 +564,8 @@ const compileLambda = (
555564
message: 'functions must be encoded as objects',
556565
})
557566
} else {
558-
const parameter = lookupWithinArgument(['parameter', '1'], possibleLambda)
559-
const body = lookupWithinArgument(['body', '2'], possibleLambda)
567+
const parameter = lookupWithinExpression(['parameter', '1'], possibleLambda)
568+
const body = lookupWithinExpression(['body', '2'], possibleLambda)
560569

561570
if (option.isNone(parameter)) {
562571
return either.makeLeft({
@@ -617,12 +626,12 @@ const locateSelf = (context: ExpressionContext) =>
617626
some: either.makeRight,
618627
})
619628

620-
const lookupWithinArgument = (
629+
export const lookupWithinExpression = (
621630
keyAliases: [Atom, ...(readonly Atom[])],
622-
argument: ObjectNode,
631+
expression: Expression,
623632
): Option<SemanticGraph> => {
624633
for (const key of keyAliases) {
625-
const result = lookupPropertyOfObjectNode(key, argument)
634+
const result = lookupPropertyOfObjectNode(key, expression)
626635
if (!option.isNone(result)) {
627636
return result
628637
}

src/language/runtime/keywords.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import {
1010
types,
1111
type KeywordElaborationResult,
1212
type KeywordModule,
13-
type ObjectNode,
1413
type SemanticGraph,
1514
} from '../semantics.js'
15+
import type { Expression } from '../semantics/expression-elaboration.js'
1616
import { lookupPropertyOfObjectNode } from '../semantics/object-node.js'
1717

1818
const unserializableFunction = () =>
@@ -88,7 +88,10 @@ export const handlers = {
8888
* Evaluates the given function, passing runtime context captured in `world`.
8989
*/
9090
'@runtime': (expression): KeywordElaborationResult => {
91-
const runtimeFunction = lookupWithinArgument(['function', '1'], expression)
91+
const runtimeFunction = lookupWithinExpression(
92+
['function', '1'],
93+
expression,
94+
)
9295
if (
9396
option.isNone(runtimeFunction) ||
9497
!isFunctionNode(runtimeFunction.value)
@@ -121,12 +124,12 @@ const allKeywords = new Set(Object.keys(handlers))
121124
export const isKeyword = (input: string): input is Keyword =>
122125
allKeywords.has(input)
123126

124-
const lookupWithinArgument = (
127+
const lookupWithinExpression = (
125128
keyAliases: [Atom, ...(readonly Atom[])],
126-
argument: ObjectNode,
129+
expression: Expression,
127130
): Option<SemanticGraph> => {
128131
for (const key of keyAliases) {
129-
const result = lookupPropertyOfObjectNode(key, argument)
132+
const result = lookupPropertyOfObjectNode(key, expression)
130133
if (!option.isNone(result)) {
131134
return result
132135
}

src/language/semantics/expression-elaboration.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ declare const _elaborated: unique symbol
1414
type Elaborated = { readonly [_elaborated]: true }
1515
export type ElaboratedSemanticGraph = WithPhantomData<SemanticGraph, Elaborated>
1616

17+
export type Expression = ObjectNode & {
18+
readonly 0: `@${string}`
19+
}
20+
21+
export const isExpression = (node: SemanticGraph): node is Expression =>
22+
typeof node === 'object' && typeof node[0] === 'string' && node[0][0] === '@'
23+
1724
export type ExpressionContext = {
1825
readonly program: SemanticGraph
1926
readonly location: KeyPath
@@ -22,7 +29,7 @@ export type ExpressionContext = {
2229
export type KeywordElaborationResult = Either<ElaborationError, SemanticGraph>
2330

2431
export type KeywordHandler = (
25-
expression: ObjectNode,
32+
expression: Expression,
2633
context: ExpressionContext,
2734
) => KeywordElaborationResult
2835

@@ -170,7 +177,7 @@ const handleObjectNodeWhichMayBeAExpression = <Keyword extends `@${string}`>(
170177
}),
171178
some: keyword =>
172179
keywordModule.handlers[keyword](
173-
makeObjectNode(possibleArguments),
180+
makeObjectNode({ ...possibleArguments, 0: keyword }),
174181
context,
175182
),
176183
},

0 commit comments

Comments
 (0)