septima-lang 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/ast-node.ts DELETED
@@ -1,269 +0,0 @@
1
- import { Span } from './location'
2
- import { Token } from './scanner'
3
- import { shouldNeverHappen } from './should-never-happen'
4
- import { switchOn } from './switch-on'
5
-
6
- export type Let = { start: Token; ident: Ident; value: AstNode }
7
-
8
- export type Import = {
9
- start: Token
10
- ident: Ident
11
- pathToImportFrom: Token
12
- }
13
-
14
- export type Literal = {
15
- tag: 'literal'
16
- type: 'str' | 'bool' | 'num' | 'sink' | 'sink!' | 'sink!!'
17
- t: Token
18
- }
19
-
20
- export type ObjectLiteralPart =
21
- | { tag: 'hardName'; k: Ident; v: AstNode }
22
- | { tag: 'computedName'; k: AstNode; v: AstNode }
23
- | { tag: 'spread'; o: AstNode }
24
-
25
- export type ArrayLiteralPart = { tag: 'element'; v: AstNode } | { tag: 'spread'; v: AstNode }
26
-
27
- export type Ident = {
28
- tag: 'ident'
29
- t: Token
30
- }
31
-
32
- export type Lambda = {
33
- tag: 'lambda'
34
- start: Token
35
- formalArgs: Ident[]
36
- body: AstNode
37
- }
38
-
39
- export type Unit = {
40
- tag: 'unit'
41
- imports: Import[]
42
- expression: AstNode
43
- }
44
-
45
- export type AstNode =
46
- | Ident
47
- | Literal
48
- | Unit
49
- | {
50
- start: Token
51
- tag: 'arrayLiteral'
52
- parts: ArrayLiteralPart[]
53
- end: Token
54
- }
55
- | {
56
- start: Token
57
- tag: 'objectLiteral'
58
- parts: ObjectLiteralPart[]
59
- end: Token
60
- }
61
- | {
62
- tag: 'binaryOperator'
63
- operator: '+' | '-' | '*' | '/' | '**' | '%' | '&&' | '||' | '>' | '<' | '>=' | '<=' | '==' | '!=' | '??'
64
- lhs: AstNode
65
- rhs: AstNode
66
- }
67
- | {
68
- tag: 'unaryOperator'
69
- operatorToken: Token
70
- operator: '+' | '-' | '!'
71
- operand: AstNode
72
- }
73
- | {
74
- tag: 'topLevelExpression'
75
- definitions: Let[]
76
- computation?: AstNode
77
- }
78
- | Lambda
79
- | {
80
- tag: 'ternary'
81
- condition: AstNode
82
- positive: AstNode
83
- negative: AstNode
84
- }
85
- | {
86
- tag: 'functionCall'
87
- actualArgs: AstNode[]
88
- callee: AstNode
89
- end: Token
90
- }
91
- | {
92
- tag: 'if'
93
- condition: AstNode
94
- positive: AstNode
95
- negative: AstNode
96
- }
97
- | {
98
- tag: 'dot'
99
- receiver: AstNode
100
- ident: Ident
101
- }
102
- | {
103
- tag: 'indexAccess'
104
- receiver: AstNode
105
- index: AstNode
106
- }
107
- | {
108
- // A sepcial AST node meant to be generated internally (needed for exporting definition from one unit to another).
109
- // Not intended to be parsed from source code. Hence, it is effectively empty, and its location cannot be
110
- // determined.
111
- tag: 'export*'
112
- }
113
-
114
- export function show(ast: AstNode | AstNode[]): string {
115
- if (Array.isArray(ast)) {
116
- return ast.map(curr => show(curr)).join(', ')
117
- }
118
- if (ast.tag === 'arrayLiteral') {
119
- const parts = ast.parts.map(p => {
120
- if (p.tag === 'element') {
121
- return show(p.v)
122
- }
123
-
124
- if (p.tag === 'spread') {
125
- return `...${show(p.v)}`
126
- }
127
-
128
- shouldNeverHappen(p)
129
- })
130
- return `[${parts.join(', ')}]`
131
- }
132
-
133
- if (ast.tag === 'binaryOperator') {
134
- return `(${show(ast.lhs)} ${ast.operator} ${show(ast.rhs)})`
135
- }
136
- if (ast.tag === 'dot') {
137
- return `${show(ast.receiver)}.${show(ast.ident)}`
138
- }
139
- if (ast.tag === 'export*') {
140
- return `(export*)`
141
- }
142
- if (ast.tag === 'ternary') {
143
- return `${show(ast.condition)} ? ${show(ast.positive)} : ${show(ast.negative)}`
144
- }
145
- if (ast.tag === 'functionCall') {
146
- return `${show(ast.callee)}(${show(ast.actualArgs)})`
147
- }
148
- if (ast.tag === 'ident') {
149
- return ast.t.text
150
- }
151
- if (ast.tag === 'if') {
152
- return `if (${show(ast.condition)}) ${show(ast.positive)} else ${show(ast.negative)}`
153
- }
154
- if (ast.tag === 'indexAccess') {
155
- return `${show(ast.receiver)}[${show(ast.index)}]`
156
- }
157
- if (ast.tag === 'lambda') {
158
- return `fun (${show(ast.formalArgs)}) ${show(ast.body)}`
159
- }
160
- if (ast.tag === 'literal') {
161
- return switchOn(ast.type, {
162
- bool: () => ast.t.text,
163
- num: () => ast.t.text,
164
- sink: () => 'sink',
165
- 'sink!': () => 'sink!',
166
- 'sink!!': () => 'sink!!',
167
- str: () => `'${ast.t.text}'`,
168
- })
169
- }
170
- if (ast.tag === 'objectLiteral') {
171
- const pairs = ast.parts.map(p => {
172
- if (p.tag === 'computedName') {
173
- return `[${show(p.k)}]: ${show(p.v)}`
174
- }
175
-
176
- if (p.tag === 'hardName') {
177
- return `${show(p.k)}: ${show(p.v)}`
178
- }
179
-
180
- if (p.tag === 'spread') {
181
- return `...${show(p.o)}`
182
- }
183
-
184
- shouldNeverHappen(p)
185
- })
186
- return `{${pairs.join(', ')}}`
187
- }
188
- if (ast.tag === 'topLevelExpression') {
189
- const defs = ast.definitions.map(d => `let ${show(d.ident)} = ${show(d.value)}`).join('; ')
190
- const sep = defs && ast.computation ? ' ' : ''
191
- return `${defs ? defs + ';' : ''}${sep}${ast.computation ? show(ast.computation) : ''}`
192
- }
193
- if (ast.tag === 'unaryOperator') {
194
- return `${ast.operator}${show(ast.operand)}`
195
- }
196
- if (ast.tag === 'unit') {
197
- const imports = ast.imports
198
- .map(imp => `import * as ${show(imp.ident)} from '${imp.pathToImportFrom.text}';`)
199
- .join('\n')
200
- return `${imports ? imports + '\n' : ''}${show(ast.expression)}`
201
- }
202
-
203
- shouldNeverHappen(ast)
204
- }
205
-
206
- export function span(ast: AstNode): Span {
207
- const ofRange = (a: Span, b: Span) => ({ from: a.from, to: b.to })
208
- const ofToken = (t: Token) => ({ from: t.location, to: { offset: t.location.offset + t.text.length - 1 } })
209
-
210
- if (ast.tag === 'arrayLiteral') {
211
- return ofRange(ofToken(ast.start), ofToken(ast.end))
212
- }
213
- if (ast.tag === 'binaryOperator') {
214
- return ofRange(span(ast.lhs), span(ast.rhs))
215
- }
216
- if (ast.tag === 'dot') {
217
- return ofRange(span(ast.receiver), span(ast.ident))
218
- }
219
- if (ast.tag === 'functionCall') {
220
- return ofRange(span(ast.callee), ofToken(ast.end))
221
- }
222
- if (ast.tag === 'ident') {
223
- return ofToken(ast.t)
224
- }
225
- if (ast.tag === 'export*') {
226
- return { from: { offset: 0 }, to: { offset: 0 } }
227
- }
228
- if (ast.tag === 'if') {
229
- return ofRange(span(ast.condition), span(ast.negative))
230
- }
231
- if (ast.tag === 'indexAccess') {
232
- return ofRange(span(ast.receiver), span(ast.index))
233
- }
234
- if (ast.tag === 'lambda') {
235
- return ofRange(ofToken(ast.start), span(ast.body))
236
- }
237
- if (ast.tag === 'ternary') {
238
- return ofRange(span(ast.condition), span(ast.negative))
239
- }
240
- if (ast.tag === 'literal') {
241
- return ofToken(ast.t)
242
- }
243
- if (ast.tag === 'objectLiteral') {
244
- return ofRange(ofToken(ast.start), ofToken(ast.end))
245
- }
246
- if (ast.tag === 'topLevelExpression') {
247
- if (ast.computation) {
248
- const d0 = ast.definitions.find(Boolean)
249
- const comp = span(ast.computation)
250
- return ofRange(d0 ? ofToken(d0.start) : comp, comp)
251
- } else if (ast.definitions.length) {
252
- const first = ast.definitions[0]
253
- const last = ast.definitions[ast.definitions.length - 1]
254
- return ofRange(ofToken(first.start), span(last.value))
255
- } else {
256
- return { from: { offset: 0 }, to: { offset: 0 } }
257
- }
258
- }
259
- if (ast.tag === 'unaryOperator') {
260
- return ofRange(ofToken(ast.operatorToken), span(ast.operand))
261
- }
262
- if (ast.tag === 'unit') {
263
- const i0 = ast.imports.find(Boolean)
264
- const exp = span(ast.expression)
265
- return ofRange(i0 ? ofToken(i0.start) : exp, exp)
266
- }
267
-
268
- shouldNeverHappen(ast)
269
- }
@@ -1,5 +0,0 @@
1
- export function extractMessage(e: unknown): string | unknown {
2
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
3
- const typed = e as { message?: string }
4
- return typed.message
5
- }
package/src/fail-me.ts DELETED
@@ -1,7 +0,0 @@
1
- export function failMe<R>(hint?: string): NonNullable<R> {
2
- if (!hint) {
3
- throw new Error(`This expression must never be evaluated`)
4
- }
5
-
6
- throw new Error(`Bad value: ${hint}`)
7
- }
@@ -1,115 +0,0 @@
1
- import { Value } from './value'
2
-
3
- export type CallEvaluator = (callable: Value, args: Value[]) => Value
4
- /**
5
- * return an implementation for array methods. The following Array methods were not implemented
6
- * as it is hard to find an intuitive immutable API due to their mutable nature:
7
- * - copyWithin
8
- * - fill
9
- * - forEach
10
- * - keys
11
- * - pop
12
- * - push
13
- * - shift
14
- * - unshift
15
- */
16
- export function findArrayMethod(arr: unknown[], index: string, callEvaluator: CallEvaluator) {
17
- const adjustedCallback =
18
- (callback: Value) =>
19
- (...args: unknown[]) =>
20
- callEvaluator(
21
- callback,
22
- args.map(x => Value.from(x)),
23
- )
24
-
25
- const adjustedPredicate =
26
- (predicate: Value) =>
27
- (...args: unknown[]) =>
28
- callEvaluator(
29
- predicate,
30
- args.map(x => Value.from(x)),
31
- ).assertBool()
32
-
33
- if (index === 'at') {
34
- return Value.foreign(n => arr.at(n.assertNum()))
35
- }
36
- if (index === 'concat') {
37
- return Value.foreign(arg => arr.concat(arg.assertArr()))
38
- }
39
- if (index === 'entries') {
40
- return Value.foreign(() => [...arr.entries()])
41
- }
42
- if (index === 'every') {
43
- return Value.foreign(predicate => arr.every(adjustedPredicate(predicate)))
44
- }
45
- if (index === 'filter') {
46
- return Value.foreign(predicate => arr.filter(adjustedPredicate(predicate)))
47
- }
48
- if (index === 'find') {
49
- return Value.foreign(predicate => arr.find(adjustedPredicate(predicate)))
50
- }
51
- if (index === 'findIndex') {
52
- return Value.foreign(predicate => arr.findIndex(adjustedPredicate(predicate)))
53
- }
54
- if (index === 'flatMap') {
55
- return Value.foreign(callback => flatten(arr.map(adjustedCallback(callback))))
56
- }
57
- if (index === 'flat') {
58
- return Value.foreign(() => flatten(arr))
59
- }
60
- if (index === 'includes') {
61
- return Value.foreign((arg: Value) => arr.some(curr => Value.from(curr).equalsTo(arg).isTrue()))
62
- }
63
- if (index === 'indexOf') {
64
- return Value.foreign(arg => arr.findIndex(curr => Value.from(curr).equalsTo(arg).isTrue()))
65
- }
66
- if (index === 'join') {
67
- return Value.foreign(arg => arr.join(arg.assertStr()))
68
- }
69
- if (index === 'lastIndexOf') {
70
- return Value.foreign(arg => {
71
- for (let i = arr.length - 1; i >= 0; --i) {
72
- if (Value.from(arr[i]).equalsTo(arg).isTrue()) {
73
- return i
74
- }
75
- }
76
- return -1
77
- })
78
- }
79
- if (index === 'length') {
80
- return Value.num(arr.length)
81
- }
82
- if (index === 'map') {
83
- return Value.foreign(callback => arr.map(adjustedCallback(callback)))
84
- }
85
- if (index === 'reverse') {
86
- return Value.foreign(() => [...arr].reverse())
87
- }
88
- if (index === 'reduce') {
89
- return Value.foreign((callback, initialValue) => arr.reduce(adjustedCallback(callback), initialValue))
90
- }
91
- if (index === 'reduceRight') {
92
- return Value.foreign((callback, initialValue) => arr.reduceRight(adjustedCallback(callback), initialValue))
93
- }
94
- if (index === 'slice') {
95
- return Value.foreign((start, end) => arr.slice(start?.assertNum(), end?.assertNum()))
96
- }
97
- if (index === 'some') {
98
- return Value.foreign(predicate => arr.some(adjustedPredicate(predicate)))
99
- }
100
- throw new Error(`Unrecognized array method: ${index}`)
101
- }
102
-
103
- function flatten(input: unknown[]) {
104
- const ret = []
105
- for (const curr of input) {
106
- const v = Value.from(curr)
107
- const unwrapped = v.unwrap()
108
- if (Array.isArray(unwrapped)) {
109
- ret.push(...unwrapped)
110
- } else {
111
- ret.push(v)
112
- }
113
- }
114
- return ret
115
- }
@@ -1,84 +0,0 @@
1
- import { Value } from './value'
2
-
3
- export function findStringMethod(s: string, indexValue: string | Value) {
4
- const index = Value.toStringOrNumber(indexValue)
5
- if (typeof index === 'number') {
6
- throw new Error(`Index is of type number - not supported`)
7
- }
8
- if (index === 'at') {
9
- return Value.foreign(n => s.at(n.assertNum()))
10
- }
11
- if (index === 'charAt') {
12
- return Value.foreign(n => s.charAt(n.assertNum()))
13
- }
14
- if (index === 'concat') {
15
- return Value.foreign(arg => s.concat(arg.assertStr()))
16
- }
17
- if (index === 'endsWith') {
18
- return Value.foreign(arg => s.endsWith(arg.assertStr()))
19
- }
20
- if (index === 'includes') {
21
- return Value.foreign(arg => s.includes(arg.assertStr()))
22
- }
23
- if (index === 'indexOf') {
24
- return Value.foreign(searchString => s.indexOf(searchString.assertStr()))
25
- }
26
- if (index === 'lastIndexOf') {
27
- return Value.foreign(searchString => s.lastIndexOf(searchString.assertStr()))
28
- }
29
- if (index === 'length') {
30
- return Value.num(s.length)
31
- }
32
- if (index === 'match') {
33
- return Value.foreign(r => s.match(r.assertStr()))
34
- }
35
- if (index === 'matchAll') {
36
- return Value.foreign(r => [...s.matchAll(new RegExp(r.assertStr(), 'g'))])
37
- }
38
- if (index === 'padEnd') {
39
- return Value.foreign((maxLength, fillString) => s.padEnd(maxLength.assertNum(), fillString?.assertStr()))
40
- }
41
- if (index === 'padStart') {
42
- return Value.foreign((maxLength, fillString) => s.padStart(maxLength.assertNum(), fillString?.assertStr()))
43
- }
44
- if (index === 'repeat') {
45
- return Value.foreign(count => s.repeat(count.assertNum()))
46
- }
47
- if (index === 'replace') {
48
- return Value.foreign((searchValue, replacer) => s.replace(searchValue.assertStr(), replacer.assertStr()))
49
- }
50
- if (index === 'replaceAll') {
51
- return Value.foreign((searchValue, replacer) => s.replaceAll(searchValue.assertStr(), replacer.assertStr()))
52
- }
53
- if (index === 'search') {
54
- return Value.foreign(searcher => s.search(searcher.assertStr()))
55
- }
56
- if (index === 'slice') {
57
- return Value.foreign((start, end) => s.slice(start?.assertNum(), end?.assertNum()))
58
- }
59
- if (index === 'split') {
60
- return Value.foreign(splitter => s.split(splitter.assertStr()))
61
- }
62
- if (index === 'startsWith') {
63
- return Value.foreign(arg => s.startsWith(arg.assertStr()))
64
- }
65
- if (index === 'substring') {
66
- return Value.foreign((start, end) => s.substring(start.assertNum(), end?.assertNum()))
67
- }
68
- if (index === 'toLowerCase') {
69
- return Value.foreign(() => s.toLowerCase())
70
- }
71
- if (index === 'toUpperCase') {
72
- return Value.foreign(() => s.toUpperCase())
73
- }
74
- if (index === 'trim') {
75
- return Value.foreign(() => s.trim())
76
- }
77
- if (index === 'trimEnd') {
78
- return Value.foreign(() => s.trimEnd())
79
- }
80
- if (index === 'trimStart') {
81
- return Value.foreign(() => s.trimStart())
82
- }
83
- throw new Error(`Unrecognized string method: ${index}`)
84
- }
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './septima'
package/src/location.ts DELETED
@@ -1,13 +0,0 @@
1
- export interface Location {
2
- readonly offset: number
3
- }
4
-
5
- export interface Span {
6
- from: Location
7
- to: Location
8
- }
9
-
10
- export interface Location2d {
11
- line: number
12
- col: number
13
- }