Skip to content

Commit 10d144c

Browse files
committed
Add a natural_number type
1 parent c5727b5 commit 10d144c

File tree

2 files changed

+59
-3
lines changed

2 files changed

+59
-3
lines changed

src/language/semantics/type-system.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { testCases } from '../../test-utilities.test.js'
22
import {
33
boolean,
44
functionType,
5+
naturalNumber,
56
nothing,
67
nullType,
78
object,
@@ -88,11 +89,13 @@ typeAssignabilitySuite('prelude types (assignable)', [
8889
[[nothing, nothing], true],
8990
[[nullType, nullType], true],
9091
[[boolean, boolean], true],
92+
[[naturalNumber, naturalNumber], true],
9193
[[string, string], true],
9294
[[object, object], true],
9395
[[functionType, functionType], true],
9496
[[something, something], true],
9597
[[nothing, nullType], true],
98+
[[nothing, naturalNumber], true],
9699
[[nothing, string], true],
97100
[[nothing, object], true],
98101
[[nothing, functionType], true],
@@ -101,6 +104,8 @@ typeAssignabilitySuite('prelude types (assignable)', [
101104
[[nullType, something], true],
102105
[[boolean, string], true],
103106
[[boolean, something], true],
107+
[[naturalNumber, string], true],
108+
[[naturalNumber, something], true],
104109
[[string, something], true],
105110
[[object, something], true],
106111
[[functionType, something], true],
@@ -109,30 +114,36 @@ typeAssignabilitySuite('prelude types (assignable)', [
109114
typeAssignabilitySuite('prelude types (not assignable)', [
110115
[[nullType, nothing], false],
111116
[[nullType, boolean], false],
117+
[[nullType, naturalNumber], false],
112118
[[nullType, object], false],
113119
[[nullType, functionType], false],
114120
[[boolean, nothing], false],
115121
[[boolean, nullType], false],
122+
[[boolean, naturalNumber], false],
116123
[[boolean, object], false],
117124
[[boolean, functionType], false],
118125
[[string, nothing], false],
119126
[[string, nullType], false],
120127
[[string, boolean], false],
128+
[[string, naturalNumber], false],
121129
[[string, object], false],
122130
[[string, functionType], false],
123131
[[object, nothing], false],
124132
[[object, nullType], false],
125133
[[object, boolean], false],
134+
[[object, naturalNumber], false],
126135
[[object, string], false],
127136
[[object, functionType], false],
128137
[[functionType, nothing], false],
129138
[[functionType, nullType], false],
130139
[[functionType, boolean], false],
140+
[[functionType, naturalNumber], false],
131141
[[functionType, string], false],
132142
[[functionType, object], false],
133143
[[something, nothing], false],
134144
[[something, nullType], false],
135145
[[something, boolean], false],
146+
[[something, naturalNumber], false],
136147
[[something, string], false],
137148
[[something, object], false],
138149
[[something, functionType], false],
@@ -143,6 +154,16 @@ typeAssignabilitySuite('custom types (assignable)', [
143154
[[makeUnionType('', ['a']), string], true],
144155
[[makeUnionType('', ['a', 'b']), string], true],
145156
[[makeUnionType('', ['a']), makeUnionType('', [string, 'b'])], true],
157+
158+
[[makeUnionType('', ['1']), naturalNumber], true],
159+
[[makeUnionType('', ['0', '1']), naturalNumber], true],
160+
[
161+
[
162+
makeUnionType('', ['9876543210']),
163+
makeUnionType('', [naturalNumber, 'not a number']),
164+
],
165+
true,
166+
],
146167
[
147168
[
148169
makeObjectType('', {

src/language/semantics/type-system/prelude-types.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ export const string = makeOpaqueType('string', {
2323
function: _ => false,
2424
// `string` can't have object types assigned to it
2525
object: _source => false,
26-
// `string` (currently) has no opaque subtypes (its only subtype is itself)
27-
opaque: source => source === string,
26+
// the only opaque subtypes of `string` are `naturalNumber` and itself
27+
opaque: source => source === string || source === naturalNumber,
2828
parameter: source =>
2929
string.isAssignableFrom(source.constraint.assignableTo),
3030
// `string` can have a union assigned to it if all of its members can be assigned to it
@@ -50,7 +50,42 @@ export const string = makeOpaqueType('string', {
5050
opaque: target => target === string,
5151
parameter: target => target.constraint.assignableTo === string,
5252
// `string` can only be assigned to a union type if `string` is one of its members
53-
union: target => target.members.has(string), // FIXME this and other checks will only work if i make sure all references to `string` use exactly this instance! otherwise i need to use TypeIDs
53+
union: target => target.members.has(string),
54+
}),
55+
})
56+
57+
export const naturalNumber = makeOpaqueType('natural_number', {
58+
isAssignableFrom: source =>
59+
matchTypeFormat(source, {
60+
function: _ => false,
61+
object: _source => false,
62+
// `naturalNumber` (currently) has no opaque subtypes (its only subtype is itself)
63+
opaque: source => source === naturalNumber,
64+
parameter: source =>
65+
naturalNumber.isAssignableFrom(source.constraint.assignableTo),
66+
// `naturalNumber` can have a union assigned to it if all of its members can be assigned to it
67+
union: source => {
68+
for (const sourceMember of source.members) {
69+
if (typeof sourceMember === 'string') {
70+
if (!/(?:0|[1-9](?:[0-9])*)+/.test(sourceMember)) {
71+
return false
72+
}
73+
} else if (!naturalNumber.isAssignableFrom(sourceMember)) {
74+
return false
75+
}
76+
}
77+
return true
78+
},
79+
}),
80+
isAssignableTo: target =>
81+
matchTypeFormat(target, {
82+
function: _ => false,
83+
object: _target => false,
84+
opaque: target => target === naturalNumber || target === string,
85+
parameter: target => target.constraint.assignableTo === naturalNumber,
86+
// `naturalNumber` can only be assigned to a union type if `naturalNumber` is one of its members
87+
union: target =>
88+
target.members.has(naturalNumber) || target.members.has(string),
5489
}),
5590
})
5691

0 commit comments

Comments
 (0)