Skip to content

Commit c457c1b

Browse files
committed
Modularize prelude/stdlib
1 parent df1cfa2 commit c457c1b

File tree

8 files changed

+701
-624
lines changed

8 files changed

+701
-624
lines changed

src/language/semantics/prelude.ts

Lines changed: 15 additions & 624 deletions
Large diffs are not rendered by default.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import either from '@matt.kantor/either'
2+
import option from '@matt.kantor/option'
3+
import { makeFunctionNode } from '../function-node.js'
4+
import { types } from '../type-system.js'
5+
import { makeFunctionType } from '../type-system/type-formats.js'
6+
import {
7+
preludeFunction,
8+
serializeOnceAppliedFunction,
9+
} from './stdlib-utilities.js'
10+
11+
export const atom = {
12+
append: preludeFunction(
13+
['atom', 'append'],
14+
{
15+
parameter: types.atom,
16+
return: makeFunctionType('', {
17+
parameter: types.atom,
18+
return: types.atom,
19+
}),
20+
},
21+
atomToAppend =>
22+
either.makeRight(
23+
makeFunctionNode(
24+
{
25+
parameter: types.atom,
26+
return: types.atom,
27+
},
28+
serializeOnceAppliedFunction(['atom', 'append'], atomToAppend),
29+
option.none,
30+
atomToAppendTo => {
31+
if (
32+
typeof atomToAppend !== 'string' ||
33+
typeof atomToAppendTo !== 'string'
34+
) {
35+
return either.makeLeft({
36+
kind: 'panic',
37+
message: 'append received a non-atom argument',
38+
})
39+
} else {
40+
return either.makeRight(atomToAppendTo + atomToAppend)
41+
}
42+
},
43+
),
44+
),
45+
),
46+
prepend: preludeFunction(
47+
['atom', 'prepend'],
48+
{
49+
parameter: types.atom,
50+
return: makeFunctionType('', {
51+
parameter: types.atom,
52+
return: types.atom,
53+
}),
54+
},
55+
atomToPrepend =>
56+
either.makeRight(
57+
makeFunctionNode(
58+
{
59+
parameter: types.atom,
60+
return: types.atom,
61+
},
62+
serializeOnceAppliedFunction(['atom', 'prepend'], atomToPrepend),
63+
option.none,
64+
atomToPrependTo => {
65+
if (
66+
typeof atomToPrepend !== 'string' ||
67+
typeof atomToPrependTo !== 'string'
68+
) {
69+
return either.makeLeft({
70+
kind: 'panic',
71+
message: 'prepend received a non-atom argument',
72+
})
73+
} else {
74+
return either.makeRight(atomToPrepend + atomToPrependTo)
75+
}
76+
},
77+
),
78+
),
79+
),
80+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import either from '@matt.kantor/either'
2+
import { type SemanticGraph } from '../semantic-graph.js'
3+
import { types } from '../type-system.js'
4+
import { preludeFunction } from './stdlib-utilities.js'
5+
6+
type BooleanNode = 'true' | 'false'
7+
const nodeIsBoolean = (node: SemanticGraph): node is BooleanNode =>
8+
node === 'true' || node === 'false'
9+
10+
export const boolean = {
11+
is: preludeFunction(
12+
['boolean', 'is'],
13+
{
14+
parameter: types.something,
15+
return: types.boolean,
16+
},
17+
argument => either.makeRight(nodeIsBoolean(argument) ? 'true' : 'false'),
18+
),
19+
not: preludeFunction(
20+
['boolean', 'not'],
21+
{
22+
parameter: types.boolean,
23+
return: types.boolean,
24+
},
25+
argument => {
26+
if (!nodeIsBoolean(argument)) {
27+
return either.makeLeft({
28+
kind: 'panic',
29+
message: 'argument was not a boolean',
30+
})
31+
} else {
32+
return either.makeRight(argument === 'true' ? 'false' : 'true')
33+
}
34+
},
35+
),
36+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import either from '@matt.kantor/either'
2+
import option from '@matt.kantor/option'
3+
import type { Atom } from '../../parsing.js'
4+
import { isFunctionNode, makeFunctionNode } from '../function-node.js'
5+
import {
6+
isObjectNode,
7+
lookupPropertyOfObjectNode,
8+
makeObjectNode,
9+
type ObjectNode,
10+
} from '../object-node.js'
11+
import { isSemanticGraph, type SemanticGraph } from '../semantic-graph.js'
12+
import { types } from '../type-system.js'
13+
import {
14+
makeFunctionType,
15+
makeObjectType,
16+
makeTypeParameter,
17+
} from '../type-system/type-formats.js'
18+
import {
19+
preludeFunction,
20+
serializeOnceAppliedFunction,
21+
} from './stdlib-utilities.js'
22+
23+
const A = makeTypeParameter('a', { assignableTo: types.something })
24+
const B = makeTypeParameter('b', { assignableTo: types.something })
25+
const C = makeTypeParameter('b', { assignableTo: types.something })
26+
27+
type TaggedNode = ObjectNode & {
28+
readonly tag: Atom
29+
readonly value: SemanticGraph
30+
}
31+
const nodeIsTagged = (node: SemanticGraph): node is TaggedNode =>
32+
isObjectNode(node) &&
33+
node['tag'] !== undefined &&
34+
(typeof node['tag'] === 'string' ||
35+
(isSemanticGraph(node['tag']) && typeof node['tag'] === 'string')) &&
36+
node['value'] !== undefined
37+
38+
export const globalFunctions = {
39+
identity: preludeFunction(
40+
['identity'],
41+
{ parameter: A, return: A },
42+
either.makeRight,
43+
),
44+
45+
apply: preludeFunction(
46+
['apply'],
47+
{
48+
// a => ((a => b) => b)
49+
parameter: A,
50+
return: makeFunctionType('', {
51+
parameter: makeFunctionType('', { parameter: A, return: B }),
52+
return: B,
53+
}),
54+
},
55+
argument =>
56+
either.makeRight(
57+
makeFunctionNode(
58+
{
59+
parameter: types.functionType,
60+
return: types.something,
61+
},
62+
serializeOnceAppliedFunction(['apply'], argument),
63+
option.none,
64+
functionToApply => {
65+
if (!isFunctionNode(functionToApply)) {
66+
return either.makeLeft({
67+
kind: 'panic',
68+
message: 'expected a function',
69+
})
70+
} else {
71+
return functionToApply(argument)
72+
}
73+
},
74+
),
75+
),
76+
),
77+
78+
// { 0: a => b, 1: b => c } => (a => c)
79+
flow: preludeFunction(
80+
['flow'],
81+
{
82+
parameter: makeObjectType('', {
83+
0: makeFunctionType('', {
84+
parameter: A,
85+
return: B,
86+
}),
87+
1: makeFunctionType('', {
88+
parameter: B,
89+
return: C,
90+
}),
91+
}),
92+
return: makeFunctionType('', {
93+
parameter: A,
94+
return: C,
95+
}),
96+
},
97+
argument => {
98+
if (!isObjectNode(argument)) {
99+
return either.makeLeft({
100+
kind: 'panic',
101+
message: '`flow` must be given an object',
102+
})
103+
} else {
104+
const argument0 = lookupPropertyOfObjectNode('0', argument)
105+
const argument1 = lookupPropertyOfObjectNode('1', argument)
106+
if (option.isNone(argument0) || option.isNone(argument1)) {
107+
return either.makeLeft({
108+
kind: 'panic',
109+
message:
110+
"`flow`'s argument must contain properties named '0' and '1'",
111+
})
112+
} else if (
113+
!isFunctionNode(argument0.value) ||
114+
!isFunctionNode(argument1.value)
115+
) {
116+
return either.makeLeft({
117+
kind: 'panic',
118+
message: "`flow`'s argument must contain functions",
119+
})
120+
} else {
121+
const function0 = argument0.value
122+
const function1 = argument1.value
123+
return either.makeRight(
124+
makeFunctionNode(
125+
{
126+
parameter: function0.signature.parameter,
127+
return: function1.signature.parameter,
128+
},
129+
serializeOnceAppliedFunction(
130+
['flow'],
131+
makeObjectNode({ 0: function0, 1: function1 }),
132+
),
133+
option.none,
134+
argument => either.flatMap(function0(argument), function1),
135+
),
136+
)
137+
}
138+
}
139+
},
140+
),
141+
142+
match: preludeFunction(
143+
['match'],
144+
{
145+
// TODO
146+
parameter: types.something,
147+
return: types.something,
148+
},
149+
cases => {
150+
if (!isObjectNode(cases)) {
151+
return either.makeLeft({
152+
kind: 'panic',
153+
message: 'match cases must be an object',
154+
})
155+
} else {
156+
return either.makeRight(
157+
makeFunctionNode(
158+
{
159+
// TODO
160+
parameter: types.something,
161+
return: types.something,
162+
},
163+
serializeOnceAppliedFunction(['match'], cases),
164+
option.none,
165+
argument => {
166+
if (!nodeIsTagged(argument)) {
167+
return either.makeLeft({
168+
kind: 'panic',
169+
message: 'argument was not tagged',
170+
})
171+
} else {
172+
const relevantCase = lookupPropertyOfObjectNode(
173+
argument.tag,
174+
cases,
175+
)
176+
if (option.isNone(relevantCase)) {
177+
return either.makeLeft({
178+
kind: 'panic',
179+
message: `case for tag '${argument.tag}' was not defined`,
180+
})
181+
} else {
182+
return !isFunctionNode(relevantCase.value)
183+
? either.makeRight(relevantCase.value)
184+
: relevantCase.value(argument.value)
185+
}
186+
}
187+
},
188+
),
189+
)
190+
}
191+
},
192+
),
193+
}

0 commit comments

Comments
 (0)