Skip to content

Commit cf5f602

Browse files
committed
Make runtime context functions serializable
1 parent d299777 commit cf5f602

File tree

1 file changed

+117
-97
lines changed

1 file changed

+117
-97
lines changed

src/language/runtime/keywords.ts

Lines changed: 117 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,127 +1,145 @@
11
import either from '@matt.kantor/either'
2-
import option from '@matt.kantor/option'
2+
import option, { type Option } from '@matt.kantor/option'
33
import { parseArgs } from 'node:util'
44
import { writeOutput } from '../cli/output.js'
55
import { keywordHandlers as compilerKeywordHandlers } from '../compiling.js'
66
import {
77
isFunctionNode,
8+
keyPathToLookupExpression,
89
makeFunctionNode,
910
makeObjectNode,
1011
readRuntimeExpression,
1112
serialize,
1213
types,
1314
type KeywordHandlers,
1415
} from '../semantics.js'
16+
import type { NonEmptyKeyPath } from '../semantics/key-path.js'
1517
import { prettyJson } from '../unparsing.js'
1618

17-
const unserializableFunction = () =>
18-
either.makeLeft({
19-
kind: 'unserializableValue',
20-
message: 'this function cannot be serialized',
21-
})
19+
const serializeFunction =
20+
(runtimeFunctionParameterName: Option<string>) =>
21+
(keyPath: NonEmptyKeyPath) => {
22+
const serialize = either.map(
23+
option.match(runtimeFunctionParameterName, {
24+
none: _ =>
25+
either.makeLeft({
26+
kind: 'unserializableValue',
27+
message: 'the runtime function cannot be serialized',
28+
}),
29+
some: either.makeRight,
30+
}),
31+
parameterName => keyPathToLookupExpression([parameterName, ...keyPath]),
32+
)
33+
return () => serialize
34+
}
2235

23-
const runtimeContext = makeObjectNode({
24-
arguments: makeObjectNode({
25-
lookup: makeFunctionNode(
26-
{
27-
parameter: types.atom,
28-
return: types.option(types.atom),
29-
},
30-
unserializableFunction,
31-
option.none,
32-
key => {
33-
if (typeof key !== 'string') {
34-
return either.makeLeft({
35-
kind: 'panic',
36-
message: 'key was not an atom',
37-
})
38-
} else {
39-
const { values: argumentValues } = parseArgs({
40-
args: process.argv,
41-
strict: false,
42-
options: {
43-
[key]: { type: 'string' },
44-
},
45-
})
46-
const argument = argumentValues[key]
47-
if (typeof argument !== 'string') {
48-
return either.makeRight(
49-
makeObjectNode({
50-
tag: 'none',
51-
value: makeObjectNode({}),
52-
}),
53-
)
36+
const runtimeContext = (runtimeFunctionParameterName: Option<string>) => {
37+
const serializeRuntimeContextFunction = serializeFunction(
38+
runtimeFunctionParameterName,
39+
)
40+
return makeObjectNode({
41+
arguments: makeObjectNode({
42+
lookup: makeFunctionNode(
43+
{
44+
parameter: types.atom,
45+
return: types.option(types.atom),
46+
},
47+
serializeRuntimeContextFunction(['arguments', 'lookup']),
48+
option.none,
49+
key => {
50+
if (typeof key !== 'string') {
51+
return either.makeLeft({
52+
kind: 'panic',
53+
message: 'key was not an atom',
54+
})
5455
} else {
55-
return either.makeRight(
56-
makeObjectNode({
57-
tag: 'some',
58-
value: argument,
59-
}),
60-
)
56+
const { values: argumentValues } = parseArgs({
57+
args: process.argv,
58+
strict: false,
59+
options: {
60+
[key]: { type: 'string' },
61+
},
62+
})
63+
const argument = argumentValues[key]
64+
if (typeof argument !== 'string') {
65+
return either.makeRight(
66+
makeObjectNode({
67+
tag: 'none',
68+
value: makeObjectNode({}),
69+
}),
70+
)
71+
} else {
72+
return either.makeRight(
73+
makeObjectNode({
74+
tag: 'some',
75+
value: argument,
76+
}),
77+
)
78+
}
6179
}
62-
}
63-
},
64-
),
65-
}),
66-
environment: makeObjectNode({
67-
lookup: makeFunctionNode(
80+
},
81+
),
82+
}),
83+
environment: makeObjectNode({
84+
lookup: makeFunctionNode(
85+
{
86+
parameter: types.atom,
87+
return: types.option(types.atom),
88+
},
89+
serializeRuntimeContextFunction(['environment', 'lookup']),
90+
option.none,
91+
key => {
92+
if (typeof key !== 'string') {
93+
return either.makeLeft({
94+
kind: 'panic',
95+
message: 'key was not an atom',
96+
})
97+
} else {
98+
const environmentVariable = process.env[key]
99+
if (environmentVariable === undefined) {
100+
return either.makeRight(
101+
makeObjectNode({
102+
tag: 'none',
103+
value: makeObjectNode({}),
104+
}),
105+
)
106+
} else {
107+
return either.makeRight(
108+
makeObjectNode({
109+
tag: 'some',
110+
value: environmentVariable,
111+
}),
112+
)
113+
}
114+
}
115+
},
116+
),
117+
}),
118+
log: makeFunctionNode(
68119
{
69-
parameter: types.atom,
70-
return: types.option(types.atom),
120+
parameter: types.something,
121+
return: types.object,
71122
},
72-
unserializableFunction,
123+
serializeRuntimeContextFunction(['log']),
73124
option.none,
74-
key => {
75-
if (typeof key !== 'string') {
125+
output => {
126+
const serializationResult = serialize(output)
127+
if (either.isLeft(serializationResult)) {
76128
return either.makeLeft({
77129
kind: 'panic',
78-
message: 'key was not an atom',
130+
message: serializationResult.value.message,
79131
})
80132
} else {
81-
const environmentVariable = process.env[key]
82-
if (environmentVariable === undefined) {
83-
return either.makeRight(
84-
makeObjectNode({
85-
tag: 'none',
86-
value: makeObjectNode({}),
87-
}),
88-
)
89-
} else {
90-
return either.makeRight(
91-
makeObjectNode({
92-
tag: 'some',
93-
value: environmentVariable,
94-
}),
95-
)
96-
}
133+
writeOutput(process.stderr, prettyJson, serializationResult.value)
134+
return either.makeRight(output)
97135
}
98136
},
99137
),
100-
}),
101-
log: makeFunctionNode(
102-
{
103-
parameter: types.something,
104-
return: types.object,
105-
},
106-
unserializableFunction,
107-
option.none,
108-
output => {
109-
const serializationResult = serialize(output)
110-
if (either.isLeft(serializationResult)) {
111-
return either.makeLeft({
112-
kind: 'panic',
113-
message: serializationResult.value.message,
114-
})
115-
} else {
116-
writeOutput(process.stderr, prettyJson, serializationResult.value)
117-
return either.makeRight(output)
118-
}
119-
},
120-
),
121-
program: makeObjectNode({
122-
start_time: new Date().toISOString(),
123-
}),
124-
})
138+
program: makeObjectNode({
139+
start_time: new Date().toISOString(),
140+
}),
141+
})
142+
}
125143

126144
export const keywordHandlers: KeywordHandlers = {
127145
...compilerKeywordHandlers,
@@ -139,7 +157,9 @@ export const keywordHandlers: KeywordHandlers = {
139157
'a function must be provided via the property `function` or `0`',
140158
})
141159
} else {
142-
const result = runtimeFunction(runtimeContext)
160+
const result = runtimeFunction(
161+
runtimeContext(runtimeFunction.parameterName),
162+
)
143163
if (either.isLeft(result)) {
144164
// The runtime function panicked or had an unavailable dependency (which results in a panic
145165
// anyway in this context).

0 commit comments

Comments
 (0)