fez-lisp 1.3.28 → 1.4.1

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/keywords.js CHANGED
@@ -39,12 +39,7 @@ export const KEYWORDS = {
39
39
  CALL_FUNCTION: 'apply',
40
40
  DEFINE_VARIABLE: 'let',
41
41
 
42
- SET_ARRAY: 'set!',
43
-
44
- LOG: 'log!',
45
- LOG_STRING: 'log-string!',
46
- LOG_CHAR: 'log-char!',
47
- CLEAR_CONSOLE: 'clear!'
42
+ SET_ARRAY: 'set!'
48
43
  }
49
44
 
50
45
  export const TYPES = {
@@ -56,3 +51,10 @@ export const RUNTIME_TYPES = {
56
51
  NUMBER: 'number',
57
52
  ARRAY: 'array'
58
53
  }
54
+ export const DEBUG = {
55
+ LOG: 'log',
56
+ ASSERT: 'assert',
57
+ ERROR: 'error'
58
+ }
59
+
60
+ export const SPECIAL_FORMS_SET = new Set(Object.values(KEYWORDS))
package/src/macros.js CHANGED
@@ -31,7 +31,7 @@ export const SUGGAR = {
31
31
  }
32
32
  export const deSuggarAst = (ast, scope) => {
33
33
  if (scope === undefined) scope = ast
34
- if (ast.length === 0) throw new SyntaxError(`No expressions to evaluate`)
34
+ if (ast.length === 0) throw new SyntaxError(`No expressions...`)
35
35
  // for (const node of ast)
36
36
  // if (node[0] && node[0][TYPE] === APPLY && node[0][VALUE] === KEYWORDS.BLOCK)
37
37
  // throw new SyntaxError(`Top level (${KEYWORDS.BLOCK}) is not allowed`)
@@ -667,11 +667,6 @@ const iron2 = (scope, exp) => {
667
667
  }
668
668
  export const replaceQuotes = (source) =>
669
669
  source
670
- .replaceAll(/\'\(/g, `(${KEYWORDS.CREATE_ARRAY} `)
671
- .replaceAll(/\`\(/g, `(${SUGGAR.CREATE_LIST} `)
672
- .replaceAll(/\(\)/g, `(${KEYWORDS.CREATE_ARRAY})`)
673
- .replaceAll(/\[\]/g, `(${KEYWORDS.CREATE_ARRAY})`)
674
- .replaceAll(/\{\}/g, `(${SUGGAR.CREATE_LIST})`)
675
670
  .replaceAll(/\[/g, `(${KEYWORDS.CREATE_ARRAY} `)
676
671
  .replaceAll(/\]/g, ')')
677
672
  .replaceAll(/\{/g, `(${SUGGAR.CREATE_LIST} `)
package/src/parser.js CHANGED
@@ -30,6 +30,8 @@ export const LISP = {
30
30
  },
31
31
  stringify: (array) => {
32
32
  if (array == undefined) return '()'
33
+ else if (typeof array === 'function') return '(lambda)'
34
+ else if (typeof array === 'boolean') return +array
33
35
  else if (typeof array === 'object')
34
36
  if (Array.isArray(array))
35
37
  return array.length
@@ -39,8 +41,6 @@ export const LISP = {
39
41
  return `(array ${array
40
42
  .map(([key, value]) => `("${key}" ${LISP.stringify(value)})`)
41
43
  .join(' ')})`
42
- else if (typeof array === 'function') return '()'
43
- else if (typeof array === 'boolean') return +array
44
44
  else return array
45
45
  },
46
46
  source: (ast) => {
@@ -61,7 +61,7 @@ export const LISP = {
61
61
  }
62
62
  return out
63
63
  }
64
- return ast.map(dfs).join(' ')
64
+ return dfs(ast)
65
65
  }
66
66
  }
67
67
  export const AST = {
package/src/utils.js CHANGED
@@ -1,6 +1,17 @@
1
1
  import std from '../lib/baked/std.js'
2
- import { comp, OPTIMIZATIONS } from './compiler.js'
3
- import { APPLY, ATOM, KEYWORDS, TYPE, VALUE, WORD } from './keywords.js'
2
+ import { compile, OPTIMIZATIONS } from './compiler.js'
3
+ import {
4
+ APPLY,
5
+ ATOM,
6
+ DEBUG,
7
+ FALSE,
8
+ KEYWORDS,
9
+ RUNTIME_TYPES,
10
+ TRUE,
11
+ TYPE,
12
+ VALUE,
13
+ WORD
14
+ } from './keywords.js'
4
15
  import { evaluate } from './evaluator.js'
5
16
  import { AST, isLeaf, LISP } from './parser.js'
6
17
  import {
@@ -12,8 +23,6 @@ import { keywords } from './interpreter.js'
12
23
  export const logError = (error) =>
13
24
  console.log('\x1b[31m', `\n${error}\n`, '\x1b[0m')
14
25
  export const logSuccess = (output) => console.log('\x1b[32m', output, '\x1b[0m')
15
- // export const replaceEmptyArrays = (source) =>
16
- // source
17
26
  export const removeNoCode = (source) =>
18
27
  source
19
28
  .replace(/;.+/g, '')
@@ -149,6 +158,7 @@ export const handleUnbalancedParens = (source) => {
149
158
  export const removeMutation = (source) => source.replace(new RegExp(/!/g), 'ǃ')
150
159
  const isDefinition = (x) =>
151
160
  x[TYPE] === APPLY && x[VALUE] === KEYWORDS.DEFINE_VARIABLE
161
+ // [[, [, libs]]] is because std is wrapped in (apply (lambda (do ...)))
152
162
  const toDeps = ([[, [, libs]]]) =>
153
163
  libs.reduce(
154
164
  (a, x, i) => a.set(x.at(1)[VALUE], { value: x, index: i }),
@@ -236,19 +246,18 @@ export const fez = (source, options = {}) => {
236
246
  const scope = deSuggarAst(parsed)
237
247
  const ast = wrapInBlock(shake(scope, std))
238
248
  // if (options.check) typeCheck(ast)
239
- if (options.compile) return comp(ast)
249
+ if (options.compile) return compile(ast)
240
250
  return evaluate(ast, env)
241
251
  } else if (Array.isArray(source)) {
242
252
  const ast = !options.mutation
243
253
  ? AST.parse(AST.stringify(source).replace(new RegExp(/!/g), 'ǃ'))
244
254
  : source
245
- if (options.compile) return comp(ast)
255
+ if (options.compile) return compile(ast)
246
256
  return evaluate(ast, env)
247
257
  } else {
248
258
  throw new Error('Source has to be either a lisp source code or an AST')
249
259
  }
250
260
  } catch (error) {
251
- // console.log(error)
252
261
  const err = error.message.replace("'[object Array]'", '(array)')
253
262
  // .replace('object', '(array)')
254
263
  logError(err)
@@ -256,38 +265,6 @@ export const fez = (source, options = {}) => {
256
265
  return err
257
266
  }
258
267
  }
259
- export const compress = (source) => {
260
- let { result, occurance } = source.split('').reduce(
261
- (acc, item) => {
262
- if (item === ')') acc.occurance++
263
- else {
264
- if (acc.occurance < 3) {
265
- acc.result += ')'.repeat(acc.occurance)
266
- acc.occurance = 0
267
- } else {
268
- acc.result += '·' + acc.occurance
269
- acc.occurance = 0
270
- }
271
- acc.result += item
272
- }
273
- return acc
274
- },
275
- { result: '', occurance: 0 }
276
- )
277
- if (occurance > 0) result += '·' + occurance
278
- return result
279
- }
280
- export const decompress = (raw) => {
281
- const suffix = [...new Set(raw.match(/·+?\d+/g))]
282
- const runes = suffix.reduce(
283
- (acc, m) => acc.split(m).join(')'.repeat(parseInt(m.substring(1)))),
284
- raw
285
- )
286
- let result = ''
287
- for (const tok of runes) result += tok
288
- return result
289
- }
290
- // shake(LISP.parse(removeNoCode(source)), std)
291
268
  export const shake = (parsed, std) => treeShake(parsed, std).concat(parsed)
292
269
  export const tree = (source, std) =>
293
270
  std
@@ -313,4 +290,178 @@ export const ast = (source, deps) =>
313
290
  deps.reduce((a, b) => a.concat(b), [])
314
291
  )
315
292
  )
293
+
316
294
  export const astWithStd = (source) => wrapInBlock(shake(prep(source), std))
295
+ export const parse = (source) =>
296
+ wrapInBlock(
297
+ shake(
298
+ deSuggarAst(
299
+ LISP.parse(
300
+ removeNoCode(
301
+ handleUnbalancedQuotes(
302
+ handleUnbalancedParens(deSuggarSource(source))
303
+ )
304
+ )
305
+ )
306
+ ),
307
+ std
308
+ )
309
+ )
310
+
311
+ const identity = (name) => [
312
+ [0, 'let'],
313
+ [1, name],
314
+ [
315
+ [0, 'lambda'],
316
+ [1, 'x'],
317
+ [1, 'x']
318
+ ]
319
+ ]
320
+ export const callStack = [KEYWORDS.CALL_FUNCTION]
321
+ export const debug = (ast) => {
322
+ callStack.length = 0
323
+ callStack.push(KEYWORDS.CALL_FUNCTION)
324
+ try {
325
+ const debugEnv = {
326
+ ...keywords,
327
+ [DEBUG.LOG]: (args, env) => {
328
+ if (args.length !== 1 && args.length !== 2)
329
+ throw new RangeError(
330
+ `Invalid number of arguments to (${DEBUG.LOG}) (or (= 1) (= 2)) (${
331
+ DEBUG.LOG
332
+ } ${stringifyArgs(args)})`
333
+ )
334
+ const expression = evaluate(args[0], env)
335
+ if (args.length === 2) {
336
+ const option = evaluate(args[1], env)
337
+ if (!Array.isArray(option)) {
338
+ throw new TypeError(
339
+ `Second argument of (${DEBUG.LOG}) must be an (${
340
+ RUNTIME_TYPES.ARRAY
341
+ }) but got (${expression}) (${DEBUG.LOG} ${stringifyArgs(args)})`
342
+ )
343
+ }
344
+ const type = option.map((x) => String.fromCharCode(x)).join('')
345
+ switch (type) {
346
+ case 'string':
347
+ case 'str':
348
+ {
349
+ if (!Array.isArray(expression))
350
+ throw new TypeError(
351
+ `Argument of (${DEBUG.LOG}) must be an (${
352
+ RUNTIME_TYPES.ARRAY
353
+ }) in the case ${type} but got (${expression}) (${
354
+ DEBUG.LOG
355
+ } ${stringifyArgs(args)})`
356
+ )
357
+ console.log(
358
+ expression.map((x) => String.fromCharCode(x)).join('')
359
+ )
360
+ }
361
+ break
362
+ case 'char':
363
+ case 'ch':
364
+ {
365
+ if (typeof expression !== 'number')
366
+ throw new TypeError(
367
+ `Argument argument of (${DEBUG.LOG}) must be a (${
368
+ RUNTIME_TYPES.NUMBER
369
+ }) in the case ${type} but got (${expression}) (${
370
+ DEBUG.LOG
371
+ } ${stringifyArgs(args)})`
372
+ )
373
+ console.log(String.fromCharCode(expression))
374
+ }
375
+
376
+ break
377
+ case '*':
378
+ console.log(expression)
379
+ break
380
+ default:
381
+ throw new TypeError(
382
+ `Invalid number of option to (${
383
+ DEBUG.LOG
384
+ }) got ${option} ${stringifyArgs(args)})`
385
+ )
386
+ }
387
+ } else console.log(expression)
388
+ return expression
389
+ },
390
+ [DEBUG.ERROR]: (args, env) => {
391
+ if (args.length !== 1)
392
+ throw new RangeError(
393
+ `Invalid number of arguments to (${DEBUG.ERROR}) (= 1 required) (${
394
+ DEBUG.ERROR
395
+ } ${stringifyArgs(args)})`
396
+ )
397
+ const expression = evaluate(args[0], env)
398
+ if (!Array.isArray(expression))
399
+ throw new TypeError(
400
+ `Argument of (${DEBUG.ERROR}) must be an (${
401
+ DEBUG.ARRAY_TYPE
402
+ }) but got (${expression}) (${DEBUG.ERROR} ${stringifyArgs(args)})`
403
+ )
404
+ throw new Error(expression.map((x) => String.fromCharCode(x)).join(''))
405
+ },
406
+ [DEBUG.ASSERT]: (args, env) => {
407
+ if (args.length < 2)
408
+ throw new RangeError(
409
+ `Invalid number of arguments for (${
410
+ DEBUG.ASSERT
411
+ }), expected (> 2 required) but got ${args.length} (${
412
+ DEBUG.ASSERT
413
+ } ${stringifyArgs(args)})`
414
+ )
415
+ if (args.length % 2 !== 0)
416
+ throw new RangeError(
417
+ `Invalid number of arguments for (${
418
+ DEBUG.ASSERT
419
+ }), expected even number of arguments but got ${args.length} (${
420
+ DEBUG.ASSERT
421
+ } ${stringifyArgs(args)})`
422
+ )
423
+ for (let i = 0; i < args.length; i += 2) {
424
+ const condition = evaluate(args[i], env)
425
+ if (condition !== FALSE && condition !== TRUE)
426
+ throw new TypeError(
427
+ `Condition of (${
428
+ DEBUG.ASSERT
429
+ }) must be ${TRUE} or ${FALSE} but got (${
430
+ DEBUG.ASSERT
431
+ } ${stringifyArgs(args)})`
432
+ )
433
+ if (condition) {
434
+ const error = args[i + 1]
435
+ if (error[0][TYPE] === APPLY && error[0][VALUE] === DEBUG.ERROR)
436
+ return evaluate(error, env)
437
+ else
438
+ throw new TypeError(
439
+ `Concequence of (${DEBUG.ASSERT}) must be (${
440
+ DEBUG.ERROR
441
+ }) but got (${DEBUG.ASSERT} ${stringifyArgs(args)})`
442
+ )
443
+ }
444
+ }
445
+ return 0
446
+ }
447
+ }
448
+ evaluate(ast, debugEnv)
449
+ } catch (error) {
450
+ const isMaxCallStack =
451
+ error.message.includes('Maximum call stack size exceeded') ||
452
+ error.message.includes('too much recursion')
453
+ if (!isMaxCallStack) {
454
+ error.message += `\n\nscope:\n(${callStack.at(-1)})`
455
+ throw error
456
+ } else logError(error.message)
457
+ }
458
+ const block = ast[1][1]
459
+ const temp = block.shift()
460
+ block.unshift(
461
+ temp,
462
+ identity(DEBUG.LOG),
463
+ identity(DEBUG.ERROR),
464
+ identity(DEBUG.ASSERT)
465
+ )
466
+ return compile(ast)
467
+ }