auto-editor 25.0.1__py3-none-any.whl → 25.2.0__py3-none-any.whl

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.
auto_editor/lang/palet.py CHANGED
@@ -5,34 +5,26 @@ The syntax is inspired by the Racket Programming language.
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
- from cmath import sqrt as complex_sqrt
9
8
  from dataclasses import dataclass
10
9
  from difflib import get_close_matches
11
10
  from fractions import Fraction
12
- from functools import reduce
13
11
  from io import StringIO
14
- from operator import add, ge, gt, is_, le, lt, mod, mul
15
- from time import sleep
16
12
  from typing import TYPE_CHECKING
17
13
 
18
14
  import numpy as np
19
- from numpy import logical_and, logical_not, logical_or, logical_xor
20
15
 
21
- from auto_editor.analyze import LevelError, mut_remove_large, mut_remove_small
16
+ from auto_editor.analyze import LevelError, mut_remove_small
22
17
  from auto_editor.lib.contracts import *
23
18
  from auto_editor.lib.data_structs import *
24
19
  from auto_editor.lib.err import MyError
25
- from auto_editor.utils.func import boolop, mut_margin
20
+ from auto_editor.utils.func import boolop
26
21
 
27
22
  if TYPE_CHECKING:
28
23
  from collections.abc import Callable
29
- from typing import Any, Literal, NoReturn
24
+ from typing import Any, NoReturn
30
25
 
31
26
  from numpy.typing import NDArray
32
27
 
33
- Number = int | float | complex | Fraction
34
- Real = int | float | Fraction
35
- BoolList = NDArray[np.bool_]
36
28
  Node = tuple
37
29
 
38
30
 
@@ -68,6 +60,8 @@ str_escape = {
68
60
  class Token:
69
61
  type: str
70
62
  value: Any
63
+ lineno: int
64
+ column: int
71
65
 
72
66
 
73
67
  class Lexer:
@@ -164,21 +158,31 @@ class Lexer:
164
158
  elif unit == "dB":
165
159
  token = DB
166
160
  elif unit != "i" and unit != "%":
167
- return Token(VAL, Sym(result + unit))
161
+ return Token(
162
+ VAL,
163
+ Sym(result + unit, self.lineno, self.column),
164
+ self.lineno,
165
+ self.column,
166
+ )
168
167
 
169
168
  try:
170
169
  if unit == "i":
171
- return Token(VAL, complex(result + "j"))
170
+ return Token(VAL, complex(result + "j"), self.lineno, self.column)
172
171
  elif unit == "%":
173
- return Token(VAL, float(result) / 100)
172
+ return Token(VAL, float(result) / 100, self.lineno, self.column)
174
173
  elif "/" in result:
175
- return Token(token, Fraction(result))
174
+ return Token(token, Fraction(result), self.lineno, self.column)
176
175
  elif "." in result:
177
- return Token(token, float(result))
176
+ return Token(token, float(result), self.lineno, self.column)
178
177
  else:
179
- return Token(token, int(result))
178
+ return Token(token, int(result), self.lineno, self.column)
180
179
  except ValueError:
181
- return Token(VAL, Sym(result + unit))
180
+ return Token(
181
+ VAL,
182
+ Sym(result + unit, self.lineno, self.column),
183
+ self.lineno,
184
+ self.column,
185
+ )
182
186
 
183
187
  def hash_literal(self) -> Token:
184
188
  if self.char == "\\":
@@ -188,7 +192,7 @@ class Lexer:
188
192
 
189
193
  char = self.char
190
194
  self.advance()
191
- return Token(VAL, Char(char))
195
+ return Token(VAL, Char(char), self.lineno, self.column)
192
196
 
193
197
  if self.char == ":":
194
198
  self.advance()
@@ -198,14 +202,14 @@ class Lexer:
198
202
  buf.write(self.char)
199
203
  self.advance()
200
204
 
201
- return Token(VAL, Keyword(buf.getvalue()))
205
+ return Token(VAL, Keyword(buf.getvalue()), self.lineno, self.column)
202
206
 
203
207
  if self.char is not None and self.char in "([{":
204
208
  brac_type = self.char
205
209
  self.advance()
206
210
  if self.char is None:
207
211
  self.close_err(f"Expected a character after #{brac_type}")
208
- return Token(VLIT, brac_pairs[brac_type])
212
+ return Token(VLIT, brac_pairs[brac_type], self.lineno, self.column)
209
213
 
210
214
  buf = StringIO()
211
215
  while self.char_is_norm():
@@ -215,10 +219,10 @@ class Lexer:
215
219
 
216
220
  result = buf.getvalue()
217
221
  if result in ("t", "T", "true"):
218
- return Token(VAL, True)
222
+ return Token(VAL, True, self.lineno, self.column)
219
223
 
220
224
  if result in ("f", "F", "false"):
221
- return Token(VAL, False)
225
+ return Token(VAL, False, self.lineno, self.column)
222
226
 
223
227
  self.error(f"Unknown hash literal `#{result}`")
224
228
 
@@ -239,17 +243,19 @@ class Lexer:
239
243
  my_str = self.string()
240
244
  if self.char == ".": # handle `object.method` syntax
241
245
  self.advance()
242
- return Token(DOT, (my_str, self.get_next_token()))
243
- return Token(VAL, my_str)
246
+ return Token(
247
+ DOT, (my_str, self.get_next_token()), self.lineno, self.column
248
+ )
249
+ return Token(VAL, my_str, self.lineno, self.column)
244
250
 
245
251
  if self.char == "'":
246
252
  self.advance()
247
- return Token(QUOTE, "'")
253
+ return Token(QUOTE, "'", self.lineno, self.column)
248
254
 
249
255
  if self.char in "(){}[]":
250
256
  _par = self.char
251
257
  self.advance()
252
- return Token(_par, _par)
258
+ return Token(_par, _par, self.lineno, self.column)
253
259
 
254
260
  if self.char in "+-":
255
261
  _peek = self.peek()
@@ -347,18 +353,27 @@ class Lexer:
347
353
  if is_method:
348
354
  from auto_editor.utils.cmdkw import parse_method
349
355
 
350
- return Token(M, parse_method(name, result, env))
356
+ return Token(
357
+ M, parse_method(name, result, env), self.lineno, self.column
358
+ )
351
359
 
352
360
  if self.char == ".": # handle `object.method` syntax
353
361
  self.advance()
354
- return Token(DOT, (Sym(result), self.get_next_token()))
362
+ return Token(
363
+ DOT,
364
+ (Sym(result, self.lineno, self.column), self.get_next_token()),
365
+ self.lineno,
366
+ self.column,
367
+ )
355
368
 
356
369
  if has_illegal:
357
370
  self.error(f"Symbol has illegal character(s): {result}")
358
371
 
359
- return Token(VAL, Sym(result))
372
+ return Token(
373
+ VAL, Sym(result, self.lineno, self.column), self.lineno, self.column
374
+ )
360
375
 
361
- return Token(EOF, "EOF")
376
+ return Token(EOF, "EOF", self.lineno, self.column)
362
377
 
363
378
 
364
379
  ###############################################################################
@@ -378,6 +393,7 @@ class Parser:
378
393
 
379
394
  def expr(self) -> Any:
380
395
  token = self.current_token
396
+ lineno, column = token.lineno, token.column
381
397
 
382
398
  if token.type == VAL:
383
399
  self.eat()
@@ -405,7 +421,7 @@ class Parser:
405
421
  if token.type == M:
406
422
  self.eat()
407
423
  name, args, kwargs = token.value
408
- _result = [Sym(name)] + args
424
+ _result = [Sym(name, lineno, column)] + args
409
425
  for key, val in kwargs.items():
410
426
  _result.append(Keyword(key))
411
427
  _result.append(val)
@@ -421,7 +437,7 @@ class Parser:
421
437
 
422
438
  if token.type == QUOTE:
423
439
  self.eat()
424
- return (Sym("quote"), self.expr())
440
+ return (Sym("quote", lineno, column), self.expr())
425
441
 
426
442
  if token.type in brac_pairs:
427
443
  self.eat()
@@ -453,238 +469,26 @@ class Parser:
453
469
 
454
470
  ###############################################################################
455
471
  # #
456
- # STANDARD LIBRARY #
472
+ # ENVIRONMENT #
457
473
  # #
458
474
  ###############################################################################
459
475
 
460
476
 
461
- is_cont = Contract("contract?", is_contract)
462
- is_iterable = Contract(
463
- "iterable?",
464
- lambda v: type(v) in (str, range, list, tuple, dict, Quoted)
465
- or isinstance(v, np.ndarray),
466
- )
467
- is_sequence = Contract(
468
- "sequence?",
469
- lambda v: type(v) in (str, range, Quoted) or isinstance(v, list | np.ndarray),
470
- )
471
- is_boolarr = Contract(
472
- "bool-array?",
473
- lambda v: isinstance(v, np.ndarray) and v.dtype.kind == "b",
474
- )
475
- bool_or_barr = Contract(
476
- "(or/c bool? bool-array?)",
477
- lambda v: type(v) is bool or is_boolarr(v),
478
- )
479
- is_keyw = Contract("keyword?", lambda v: type(v) is QuotedKeyword)
480
-
477
+ class Syntax:
478
+ __slots__ = "syn"
481
479
 
482
- @dataclass(slots=True)
483
- class OutputPort:
484
- name: str
485
- port: Any
486
- write: Any
487
- closed: bool
480
+ def __init__(self, syn: Callable[[Env, Node], Any]):
481
+ self.syn = syn
488
482
 
489
- def close(self) -> None:
490
- if not self.closed:
491
- self.closed = True
492
- self.port.close()
483
+ def __call__(self, env: Env, node: Node) -> Any:
484
+ return self.syn(env, node)
493
485
 
494
486
  def __str__(self) -> str:
495
- return f"#<output-port:{self.name}>"
487
+ return "#<syntax>"
496
488
 
497
489
  __repr__ = __str__
498
490
 
499
491
 
500
- def initOutPort(name: str) -> OutputPort | Literal[False]:
501
- try:
502
- port = open(name, "w", encoding="utf-8")
503
- except Exception:
504
- return False
505
- return OutputPort(name, port, port.write, False)
506
-
507
-
508
- def raise_(msg: str | Exception) -> NoReturn:
509
- raise MyError(msg)
510
-
511
-
512
- def is_equal(a: object, b: object) -> bool:
513
- if isinstance(a, np.ndarray) and isinstance(b, np.ndarray):
514
- return np.array_equal(a, b)
515
- return type(a) == type(b) and a == b
516
-
517
-
518
- def equal_num(*values: object) -> bool:
519
- return all(values[0] == val for val in values[1:])
520
-
521
-
522
- def minus(*vals: Number) -> Number:
523
- if len(vals) == 1:
524
- return -vals[0]
525
- return reduce(lambda a, b: a - b, vals)
526
-
527
-
528
- def num_div(z: Number, *w: Number) -> Number:
529
- if len(w) == 0:
530
- w = (z,)
531
- z = 1
532
-
533
- for num in w:
534
- if num == 0:
535
- raise MyError("/: division by zero")
536
-
537
- z /= num
538
-
539
- return z
540
-
541
-
542
- def int_div(n: int, *m: int) -> int:
543
- if 0 in m:
544
- raise MyError("div: division by zero")
545
-
546
- return reduce(lambda a, b: a // b, m, n)
547
-
548
-
549
- def _sqrt(v: Number) -> Number:
550
- r = complex_sqrt(v)
551
- if r.imag == 0:
552
- if int(r.real) == r.real:
553
- return int(r.real)
554
- return r.real
555
- return r
556
-
557
-
558
- def _xor(*vals: Any) -> bool | BoolList:
559
- if is_boolarr(vals[0]):
560
- check_args("xor", vals, (2, None), (is_boolarr,))
561
- return reduce(lambda a, b: boolop(a, b, logical_xor), vals)
562
- check_args("xor", vals, (2, None), (is_bool,))
563
- return reduce(lambda a, b: a ^ b, vals)
564
-
565
-
566
- def string_ref(s: str, ref: int) -> Char:
567
- try:
568
- return Char(s[ref])
569
- except IndexError:
570
- raise MyError(f"string index {ref} is out of range")
571
-
572
-
573
- def number_to_string(val: Number) -> str:
574
- if isinstance(val, complex):
575
- join = "" if val.imag < 0 else "+"
576
- return f"{val.real}{join}{val.imag}i"
577
- return f"{val}"
578
-
579
-
580
- def palet_join(v: Any, s: str) -> str:
581
- try:
582
- return s.join(v)
583
- except Exception:
584
- raise MyError("join: expected string?")
585
-
586
-
587
- dtype_map = {
588
- Sym("bool"): np.bool_,
589
- Sym("int8"): np.int8,
590
- Sym("int16"): np.int16,
591
- Sym("int32"): np.int32,
592
- Sym("int64"): np.int64,
593
- Sym("uint8"): np.uint8,
594
- Sym("uint16"): np.uint16,
595
- Sym("uint32"): np.uint32,
596
- Sym("uint64"): np.uint64,
597
- Sym("float32"): np.float32,
598
- Sym("float64"): np.float64,
599
- }
600
-
601
-
602
- def _dtype_to_np(dtype: Sym) -> type[np.generic]:
603
- np_dtype = dtype_map.get(dtype)
604
- if np_dtype is None:
605
- raise MyError(f"Invalid array dtype: {dtype}")
606
- return np_dtype
607
-
608
-
609
- def array_proc(dtype: Sym, *vals: Any) -> np.ndarray:
610
- try:
611
- return np.array(vals, dtype=_dtype_to_np(dtype))
612
- except OverflowError:
613
- raise MyError(f"number too large to be converted to {dtype}")
614
-
615
-
616
- def make_array(dtype: Sym, size: int, v: int = 0) -> np.ndarray:
617
- try:
618
- return np.array([v] * size, dtype=_dtype_to_np(dtype))
619
- except OverflowError:
620
- raise MyError(f"number too large to be converted to {dtype}")
621
-
622
-
623
- def minclip(oarr: BoolList, _min: int, /) -> BoolList:
624
- arr = np.copy(oarr)
625
- mut_remove_small(arr, _min, replace=1, with_=0)
626
- return arr
627
-
628
-
629
- def mincut(oarr: BoolList, _min: int, /) -> BoolList:
630
- arr = np.copy(oarr)
631
- mut_remove_small(arr, _min, replace=0, with_=1)
632
- return arr
633
-
634
-
635
- def maxclip(oarr: BoolList, _min: int, /) -> BoolList:
636
- arr = np.copy(oarr)
637
- mut_remove_large(arr, _min, replace=1, with_=0)
638
- return arr
639
-
640
-
641
- def maxcut(oarr: BoolList, _min: int, /) -> BoolList:
642
- arr = np.copy(oarr)
643
- mut_remove_large(arr, _min, replace=0, with_=1)
644
- return arr
645
-
646
-
647
- def margin(oarr: BoolList, start: int, end: int | None = None, /) -> BoolList:
648
- arr = np.copy(oarr)
649
- if end is None:
650
- mut_margin(arr, start, start)
651
- else:
652
- mut_margin(arr, start, end)
653
- return arr
654
-
655
-
656
- def vector_set(vec: list, pos: int, v: Any) -> None:
657
- try:
658
- vec[pos] = v
659
- except IndexError:
660
- raise MyError(f"vector-set: Invalid index {pos}")
661
-
662
-
663
- def vector_extend(vec: list, *more_vecs: list) -> None:
664
- for more in more_vecs:
665
- vec.extend(more)
666
-
667
-
668
- def list_append(*v: Quoted) -> Quoted:
669
- result = Quoted(tuple())
670
- for item in v:
671
- result.val = result.val + item.val
672
- return result
673
-
674
-
675
- def palet_map(proc: Proc, seq: Any) -> Any:
676
- if type(seq) is str:
677
- return str(map(proc, seq))
678
- if type(seq) is Quoted:
679
- return Quoted(tuple(map(proc, seq.val)))
680
- if isinstance(seq, list | range):
681
- return list(map(proc, seq))
682
- elif isinstance(seq, np.ndarray):
683
- vectorized_proc = np.vectorize(proc)
684
- return vectorized_proc(seq)
685
- return proc(seq)
686
-
687
-
688
492
  def ref(seq: Any, ref: int) -> Any:
689
493
  try:
690
494
  if type(seq) is str:
@@ -708,743 +512,19 @@ def p_slice(
708
512
  return seq[start:end:step]
709
513
 
710
514
 
711
- def splice(
712
- arr: NDArray, v: int, start: int | None = None, end: int | None = None
713
- ) -> None:
714
- arr[start:end] = v
715
-
716
-
717
- def palet_hash(*args: Any) -> dict:
718
- result = {}
719
- if len(args) % 2 == 1:
720
- raise MyError("hash: number of args must be even")
721
- for key, item in zip(args[0::2], args[1::2]):
722
- result[key] = item
723
- return result
724
-
725
-
726
- def hash_ref(h: dict, k: object) -> object:
727
- try:
728
- return h[k]
729
- except Exception:
730
- raise MyError("hash-ref: invalid key")
731
-
732
-
733
- def hash_set(h: dict, k: object, v: object) -> None:
734
- h[k] = v
735
-
736
-
737
- def hash_remove(h: dict, v: object) -> None:
738
- try:
739
- del h[v]
740
- except Exception:
741
- pass
742
-
743
-
744
- def palet_assert(expr: object, msg: str | bool = False) -> None:
745
- if expr is not True:
746
- raise MyError("assert-error" if msg is False else f"assert-error: {msg}")
747
-
748
-
749
- def palet_system(cmd: str) -> bool:
750
- import subprocess
751
-
752
- try:
753
- return subprocess.run(cmd, shell=True).returncode == 0
754
- except Exception:
755
- return False
756
-
757
-
758
- ###############################################################################
759
- # #
760
- # ENVIRONMENT #
761
- # #
762
- ###############################################################################
763
-
764
-
765
- class UserProc(Proc):
766
- """A user-defined procedure."""
767
-
768
- __slots__ = ("env", "name", "parms", "body", "contracts", "arity")
769
-
770
- def __init__(
771
- self,
772
- env: Env,
773
- name: str,
774
- parms: list[str],
775
- contracts: tuple[Any, ...],
776
- body: Node,
777
- ):
778
- self.env = env
779
- self.name = name
780
- self.parms = parms
781
- self.body = body
782
- self.contracts = contracts
783
-
784
- if parms and parms[-1] == "...":
785
- parms.pop()
786
- self.arity: tuple[int, int | None] = len(parms) - 1, None
787
- else:
788
- self.arity = len(parms), len(parms)
789
-
790
- def __call__(self, *args: Any) -> Any:
791
- check_args(self.name, args, self.arity, self.contracts)
792
-
793
- if self.arity[1] is None:
794
- args = tuple(
795
- list(args[: len(self.parms) - 1]) + [list(args[len(self.parms) - 1 :])]
796
- )
797
-
798
- inner_env = Env(dict(zip(self.parms, args)), self.env)
799
-
800
- for item in self.body[0:-1]:
801
- my_eval(inner_env, item)
802
-
803
- return my_eval(inner_env, self.body[-1])
804
-
805
-
806
- @dataclass(slots=True)
807
- class KeywordUserProc:
808
- env: Env
809
- name: str
810
- parms: list[str]
811
- kw_parms: list[str]
812
- body: Node
813
- arity: tuple[int, None]
814
- contracts: list[Any] | None = None
815
-
816
- def __call__(self, *args: Any, **kwargs: Any) -> Any:
817
- env = {}
818
- all_parms = self.parms + self.kw_parms
819
- for i, arg in enumerate(args):
820
- if i >= len(all_parms):
821
- raise MyError("Too many arguments")
822
- env[all_parms[i]] = arg
823
-
824
- for key, val in kwargs.items():
825
- if key in env:
826
- raise MyError(
827
- f"Keyword: {key} already fulfilled by positional argument."
828
- )
829
- else:
830
- env[key] = val
831
-
832
- inner_env = Env(env, self.env)
833
-
834
- for item in self.body[0:-1]:
835
- my_eval(inner_env, item)
836
-
837
- return my_eval(inner_env, self.body[-1])
838
-
839
- def __str__(self) -> str:
840
- return self.name
841
-
842
- def __repr__(self) -> str:
843
- return f"#<kw-proc:{self.name}>"
844
-
845
-
846
- class Syntax:
847
- __slots__ = "syn"
848
-
849
- def __init__(self, syn: Callable[[Env, Node], Any]):
850
- self.syn = syn
851
-
852
- def __call__(self, env: Env, node: Node) -> Any:
853
- return self.syn(env, node)
854
-
855
- def __str__(self) -> str:
856
- return "#<syntax>"
857
-
858
- __repr__ = __str__
859
-
860
-
861
- def check_for_syntax(env: Env, node: Node) -> tuple[Sym, Any]:
862
- name = node[0]
863
- if len(node) < 2:
864
- raise MyError(f"{name}: bad syntax")
865
-
866
- if len(node) == 2:
867
- raise MyError(f"{name}: missing body")
868
-
869
- assert isinstance(node[1], tuple)
870
- assert isinstance(node[1][0], tuple)
871
-
872
- var = node[1][0][0]
873
- if type(var) is not Sym:
874
- raise MyError(f"{name}: binding must be an identifier")
875
- my_iter = my_eval(env, node[1][0][1])
876
-
877
- if not is_iterable(my_iter):
878
- if type(my_iter) is int:
879
- return var, range(my_iter)
880
- raise MyError(f"{name}: got non-iterable in iter slot")
881
-
882
- return var, my_iter
883
-
884
-
885
- def syn_lambda(env: Env, node: Node) -> UserProc:
886
- if len(node) < 3:
887
- raise MyError(f"{node[0]}: too few terms")
888
-
889
- if type(node[1]) is not tuple:
890
- raise MyError(f"{node[0]}: bad syntax")
891
-
892
- parms: list[str] = []
893
- for item in node[1]:
894
- if type(item) is not Sym:
895
- raise MyError(f"{node[0]}: must be an identifier")
896
-
897
- parms.append(f"{item}")
898
-
899
- return UserProc(env, "", parms, (), node[2:])
900
-
901
-
902
- def syn_define(env: Env, node: Node) -> None:
903
- if len(node) < 3:
904
- raise MyError(f"{node[0]}: too few terms")
905
-
906
- if type(node[1]) is tuple:
907
- term = node[1]
908
- body = node[2:]
909
-
910
- if not term or type(term[0]) is not Sym:
911
- raise MyError(f"{node[0]}: proc-binding must be an identifier")
912
-
913
- n = term[0].val
914
- parms: list[str] = []
915
- kparms: list[str] = []
916
- kw_only = False
917
-
918
- for item in term[1:]:
919
- if kw_only:
920
- if type(item) is Sym:
921
- raise MyError(f"{node[0]}: {item} must be a keyword")
922
- if type(item) is not Keyword:
923
- raise MyError(f"{node[0]}: must be a keyword")
924
- kparms.append(item.val)
925
- else:
926
- if type(item) is Keyword:
927
- kw_only = True
928
- kparms.append(item.val)
929
- elif type(item) is Sym:
930
- parms.append(item.val)
931
- else:
932
- raise MyError(f"{node[0]}: must be an identifier")
933
-
934
- if kw_only:
935
- env[n] = KeywordUserProc(env, n, parms, kparms, body, (len(parms), None))
936
- else:
937
- env[n] = UserProc(env, n, parms, (), body)
938
- return None
939
-
940
- elif type(node[1]) is not Sym:
941
- raise MyError(f"{node[0]}: must be an identifier")
942
-
943
- if len(node) > 3:
944
- raise MyError(f"{node[0]}: multiple expressions after identifier")
945
-
946
- n = node[1].val
947
-
948
- if (
949
- type(node[2]) is tuple
950
- and node[2]
951
- and type(node[2][0]) is Sym
952
- and node[2][0].val in ("lambda", "λ")
953
- ):
954
- terms = node[2][1]
955
- body = node[2][2:]
956
-
957
- parms = []
958
- for item in terms:
959
- if type(item) is not Sym:
960
- raise MyError(f"{node[0]}: must be an identifier")
961
-
962
- parms.append(f"{item}")
963
-
964
- env[n] = UserProc(env, n, parms, (), body)
965
-
966
- else:
967
- for item in node[2:-1]:
968
- my_eval(env, item)
969
- env[n] = my_eval(env, node[-1])
970
-
971
-
972
- def syn_definec(env: Env, node: Node) -> None:
973
- if len(node) < 3:
974
- raise MyError(f"{node[0]}: too few terms")
975
-
976
- if type(node[1]) is not tuple:
977
- raise MyError(f"{node[0]} only allows procedure declarations")
978
-
979
- if not node[1] or type(node[1][0]) is not Sym:
980
- raise MyError(f"{node[0]}: bad proc-binding syntax")
981
-
982
- n = node[1][0].val
983
-
984
- contracts: list[Any] = []
985
- parms: list[str] = []
986
- for item in node[1][1:]:
987
- if item == Sym("->"):
988
- break
989
- if type(item) is not tuple or len(item) != 2:
990
- raise MyError(f"{node[0]}: bad var-binding syntax")
991
- if type(item[0]) is not Sym:
992
- raise MyError(f"{node[0]}: binding must be identifier")
993
-
994
- con = my_eval(env, item[1])
995
- if not is_cont(con):
996
- raise MyError(f"{node[0]}: {print_str(con)} is not a valid contract")
997
-
998
- parms.append(f"{item[0]}")
999
- contracts.append(con)
1000
-
1001
- env[n] = UserProc(env, n, parms, tuple(contracts), node[2:])
1002
- return None
1003
-
1004
-
1005
- def guard_term(node: Node, n: int, u: int) -> None:
1006
- if n == u:
1007
- if len(node) != n:
1008
- raise MyError(
1009
- f"{node[0]}: Expects exactly {n-1} term{'s' if n > 2 else ''}"
1010
- )
1011
- return None
1012
- if len(node) < n:
1013
- raise MyError(f"{node[0]}: Expects at least {n-1} term{'s' if n > 2 else ''}")
1014
- if len(node) > u:
1015
- raise MyError(f"{node[0]}: Expects at most {u-1} term{'s' if u > 2 else ''}")
1016
-
1017
-
1018
- def syn_set(env: Env, node: Node) -> None:
1019
- guard_term(node, 3, 3)
1020
-
1021
- if type(node[1]) is Sym:
1022
- name = node[1].val
1023
- if name not in env:
1024
- raise MyError(f"{node[0]}: Can't set variable `{name}` before definition")
1025
- env[name] = my_eval(env, node[2])
1026
- return None
1027
-
1028
- if type(node[1]) is tuple and len(node[1]) == 3 and node[1][0] == Sym("@r"):
1029
- base = my_eval(env, node[1][1])
1030
- name = node[1][2].val
1031
- for i, item in enumerate(base.attrs[0::2]):
1032
- if name == item:
1033
- result = my_eval(env, node[2])
1034
- check_args(item, (result,), (1, 1), (base.attrs[i * 2 + 1],))
1035
- base.values[i] = result
1036
- return None
1037
-
1038
- raise MyError(f"{node[0]}: {base.name} has no attribute `{name}`")
1039
-
1040
- raise MyError(f"{node[0]}: Expected identifier, got {print_str(node[1])}")
1041
-
1042
-
1043
- def syn_incf(env: Env, node: Node) -> None:
1044
- guard_term(node, 2, 3)
1045
-
1046
- incre_by = 1
1047
- if len(node) == 3:
1048
- incre_by = my_eval(env, node[2])
1049
- if not is_num(incre_by):
1050
- raise MyError(f"{node[0]}: Expected number? got: {print_str(incre_by)}")
1051
-
1052
- if type(node[1]) is Sym:
1053
- name = node[1].val
1054
-
1055
- if type(env[name]) is NotFound:
1056
- raise MyError(f"{node[0]}: `{name}` is not defined")
1057
- if not is_num(env[name]):
1058
- raise MyError(f"{node[0]}: `{name}` is not a number?")
1059
- env[name] += incre_by
1060
- return None
1061
-
1062
- if type(node[1]) is tuple and len(node[1]) == 3 and node[1][0] == Sym("@r"):
1063
- base = my_eval(env, node[1][1])
1064
- if type(base) is not PaletClass:
1065
- raise MyError(f"{node[0]}: must be a class instance")
1066
- if type(node[1][2]) is not Sym:
1067
- raise MyError(f"{node[0]}: class attribute must be an identifier")
1068
- name = node[1][2].val
1069
- for i, item in enumerate(base.attrs[0::2]):
1070
- if name == item:
1071
- if not is_num(base.values[i]):
1072
- raise MyError(f"{node[0]}: `{name}` is not a number?")
1073
-
1074
- check_args(
1075
- name, (base.values[i] + incre_by,), (1, 1), (base.attrs[i * 2 + 1],)
1076
- )
1077
- base.values[i] += incre_by
1078
- return None
1079
- raise MyError(f"{node[0]}: {base.name} has no attribute `{name}`")
1080
-
1081
- raise MyError(f"{node[0]}: Expected identifier, got {print_str(node[1])}")
1082
-
1083
-
1084
- def syn_decf(env: Env, node: Node) -> None:
1085
- guard_term(node, 2, 3)
1086
-
1087
- incre_by = 1
1088
- if len(node) == 3:
1089
- incre_by = my_eval(env, node[2])
1090
- if not is_num(incre_by):
1091
- raise MyError(f"{node[0]}: Expected number? got: {print_str(incre_by)}")
1092
-
1093
- if type(node[1]) is Sym:
1094
- name = node[1].val
1095
-
1096
- if type(env[name]) is NotFound:
1097
- raise MyError(f"{node[0]}: `{name}` is not defined")
1098
- if not is_num(env[name]):
1099
- raise MyError(f"{node[0]}: `{name}` is not a number?")
1100
- env[name] -= incre_by
1101
- return None
1102
-
1103
- if type(node[1]) is tuple and len(node[1]) == 3 and node[1][0] == Sym("@r"):
1104
- base = my_eval(env, node[1][1])
1105
- if type(base) is not PaletClass:
1106
- raise MyError(f"{node[0]}: must be a class instance")
1107
- if type(node[1][2]) is not Sym:
1108
- raise MyError(f"{node[0]}: class attribute must be an identifier")
1109
- name = node[1][2].val
1110
- for i, item in enumerate(base.attrs[0::2]):
1111
- if name == item:
1112
- if not is_num(base.values[i]):
1113
- raise MyError(f"{node[0]}: `{name}` is not a number?")
1114
-
1115
- check_args(
1116
- name, (base.values[i] - incre_by,), (1, 1), (base.attrs[i * 2 + 1],)
1117
- )
1118
- base.values[i] -= incre_by
1119
- return None
1120
- raise MyError(f"{node[0]}: {base.name} has no attribute `{name}`")
1121
-
1122
- raise MyError(f"{node[0]}: Expected identifier, got {print_str(node[1])}")
1123
-
1124
-
1125
- def syn_strappend(env: Env, node: Node) -> None:
1126
- guard_term(node, 3, 3)
1127
-
1128
- if type(node[1]) is not Sym:
1129
- raise MyError(f"{node[0]}: Expected identifier, got {print_str(node[1])}")
1130
- name = node[1].val
1131
-
1132
- if type(env[name]) is NotFound:
1133
- raise MyError(f"{node[0]}: `{name}` is not defined")
1134
- if not is_str(env[name]):
1135
- raise MyError(f"{node[0]}: `{name}` is not a string?")
1136
-
1137
- if not is_str(num := my_eval(env, node[2])):
1138
- raise MyError(f"{node[0]}: Expected string? got: {print_str(num)}")
1139
- env[name] += num
1140
-
1141
-
1142
- def syn_for(env: Env, node: Node) -> None:
1143
- var, my_iter = check_for_syntax(env, node)
1144
-
1145
- if isinstance(my_iter, np.ndarray) and my_iter.dtype == np.bool_:
1146
- for item in my_iter:
1147
- env[var.val] = int(item)
1148
- for c in node[2:]:
1149
- my_eval(env, c)
1150
- else:
1151
- for item in my_iter:
1152
- env[var.val] = item
1153
- for c in node[2:]:
1154
- my_eval(env, c)
1155
-
1156
-
1157
- def syn_for_items(env: Env, node: Node) -> None:
1158
- if len(node) < 2:
1159
- raise MyError(f"{node[0]}: bad syntax")
1160
-
1161
- if type(node[1]) is not tuple or len(node[1]) != 3:
1162
- raise MyError(f"{node[0]}: Invalid id body")
1163
-
1164
- key, val, dic = node[1]
1165
- if type(key) is not Sym or type(val) is not Sym:
1166
- raise MyError(f"{node[0]}: key and val must be identifiers")
1167
-
1168
- dic = my_eval(env, dic)
1169
- if type(dic) is not dict:
1170
- raise MyError(f"{node[0]}: dict must be a hash?")
1171
-
1172
- for k, v in dic.items():
1173
- env[key.val] = k
1174
- env[val.val] = v
1175
- for c in node[2:]:
1176
- my_eval(env, c)
1177
-
1178
-
1179
- def syn_quote(env: Env, node: Node) -> Any:
1180
- guard_term(node, 2, 2)
1181
- if type(node[1]) is Keyword:
1182
- return QuotedKeyword(node[1])
1183
- if type(node[1]) is tuple:
1184
- return Quoted(node[1])
1185
- return node[1]
1186
-
1187
-
1188
- def syn_if(env: Env, node: Node) -> Any:
1189
- guard_term(node, 4, 4)
1190
- test_expr = my_eval(env, node[1])
1191
-
1192
- if type(test_expr) is not bool:
1193
- raise MyError(
1194
- f"{node[0]} test-expr: expected bool?, got {print_str(test_expr)}"
1195
- )
1196
-
1197
- return my_eval(env, node[2] if test_expr else node[3])
1198
-
1199
-
1200
- def syn_when(env: Env, node: Node) -> Any:
1201
- if len(node) < 3:
1202
- raise MyError(f"{node[0]}: Expected at least 2 terms")
1203
- test_expr = my_eval(env, node[1])
1204
-
1205
- if type(test_expr) is not bool:
1206
- raise MyError(
1207
- f"{node[0]} test-expr: expected bool?, got {print_str(test_expr)}"
1208
- )
1209
-
1210
- if test_expr:
1211
- for item in node[2:-1]:
1212
- my_eval(env, item)
1213
- return my_eval(env, node[-1])
1214
- return None
1215
-
1216
-
1217
- def syn_and(env: Env, node: Node) -> Any:
1218
- if len(node) == 1:
1219
- raise MyError(f"{node[0]}: Expected at least 1 term")
1220
-
1221
- first = my_eval(env, node[1])
1222
- if first is False:
1223
- return False
1224
- if first is True:
1225
- for n in node[2:]:
1226
- val = my_eval(env, n)
1227
- if val is False:
1228
- return False
1229
- if val is not True:
1230
- raise MyError(f"{node[0]} args must be bool?")
1231
- return True
1232
-
1233
- if is_boolarr(first):
1234
- vals = [first] + [my_eval(env, n) for n in node[2:]]
1235
- check_args(node[0], vals, (2, None), (is_boolarr,))
1236
- return reduce(lambda a, b: boolop(a, b, logical_and), vals)
1237
-
1238
- raise MyError(f"{node[0]} expects (or/c bool? bool-array?)")
1239
-
1240
-
1241
- def syn_or(env: Env, node: Node) -> Any:
1242
- if len(node) == 1:
1243
- raise MyError(f"{node[0]}: Expected at least 1 term")
1244
-
1245
- first = my_eval(env, node[1])
1246
- if first is True:
1247
- return True
1248
- if first is False:
1249
- for n in node[2:]:
1250
- val = my_eval(env, n)
1251
- if val is True:
1252
- return True
1253
- if val is not False:
1254
- raise MyError(f"{node[0]} args must be bool?")
1255
- return False
1256
-
1257
- if is_boolarr(first):
1258
- vals = [first] + [my_eval(env, n) for n in node[2:]]
1259
- check_args(node[0], vals, (2, None), (is_boolarr,))
1260
- return reduce(lambda a, b: boolop(a, b, logical_or), vals)
1261
-
1262
- raise MyError(f"{node[0]} expects (or/c bool? bool-array?)")
1263
-
1264
-
1265
- def syn_delete(env: Env, node: Node) -> None:
1266
- guard_term(node, 2, 2)
1267
- if type(node[1]) is not Sym:
1268
- raise MyError(f"{node[0]}: Expected identifier for first term")
1269
-
1270
- del env[node[1].val]
1271
-
1272
-
1273
- def syn_rename(env: Env, node: Node) -> None:
1274
- guard_term(node, 3, 3)
1275
-
1276
- first = node[1]
1277
- if type(first) is not Sym:
1278
- raise MyError(f"{node[0]}: Expected identifier for first term")
1279
-
1280
- sec = node[2]
1281
- if type(sec) is not Sym:
1282
- raise MyError(f"{node[0]}: Expected identifier for second term")
1283
-
1284
- if first.val not in env:
1285
- raise MyError(f"{node[0]}: Original identifier does not exist")
1286
-
1287
- env[sec.val] = env[first.val]
1288
- del env[first.val]
1289
-
1290
-
1291
- def syn_cond(env: Env, node: Node) -> Any:
1292
- for test_expr in node[1:]:
1293
- if type(test_expr) is not tuple or not test_expr:
1294
- raise MyError(f"{node[0]}: bad syntax, clause is not a test-value pair")
1295
-
1296
- if test_expr[0] == Sym("else"):
1297
- if len(test_expr) == 1:
1298
- raise MyError(f"{node[0]}: missing expression in else clause")
1299
- test_clause = True
1300
- else:
1301
- test_clause = my_eval(env, test_expr[0])
1302
- if type(test_clause) is not bool:
1303
- raise MyError(
1304
- f"{node[0]} test-expr: expected bool?, got {print_str(test_clause)}"
1305
- )
1306
-
1307
- if test_clause:
1308
- if len(test_expr) == 1:
1309
- return True
1310
-
1311
- for rest_clause in test_expr[1:-1]:
1312
- my_eval(env, rest_clause)
1313
- return my_eval(env, test_expr[-1])
1314
-
1315
- return None
1316
-
1317
-
1318
- def syn_case(env: Env, node: Node) -> Any:
1319
- val_expr = my_eval(env, node[1])
1320
- for case_clause in node[2:]:
1321
- if type(case_clause) is not tuple or len(case_clause) != 2:
1322
- raise MyError("case: bad syntax")
1323
- if type(case_clause[0]) is tuple:
1324
- for case in case_clause[0]:
1325
- if is_equal(case, val_expr):
1326
- return my_eval(env, case_clause[1])
1327
- elif type(case_clause[0]) is Sym and case_clause[0].val == "else":
1328
- return my_eval(env, case_clause[1])
1329
- else:
1330
- raise MyError("case: bad syntax")
1331
- return None
1332
-
1333
-
1334
- def syn_let(env: Env, node: Node) -> Any:
1335
- if len(node) < 2:
1336
- raise MyError(f"{node[0]}: Expected at least 1 term")
1337
-
1338
- if type(node[1]) is Sym:
1339
- raise MyError(f"{node[0]}: Named-let form is not supported")
1340
-
1341
- for var_ids in node[1]:
1342
- if type(var_ids) is not tuple or len(var_ids) != 2:
1343
- raise MyError(f"{node[0]}: Expected two terms: `id` and `val-expr`")
1344
-
1345
- new_maps: dict[str, Any] = {}
1346
- for var, val in node[1]:
1347
- if type(var) is not Sym:
1348
- raise MyError(f"{node[0]}: Expected symbol for `id` term")
1349
- new_maps[var.val] = my_eval(env, val)
1350
-
1351
- inner_env = Env(new_maps, env)
1352
- for item in node[2:-1]:
1353
- my_eval(inner_env, item)
1354
- return my_eval(inner_env, node[-1])
1355
-
1356
-
1357
- def syn_let_star(env: Env, node: Node) -> Any:
1358
- if len(node) < 2:
1359
- raise MyError(f"{node[0]}: Expected at least 1 term")
1360
-
1361
- for var_ids in node[1]:
1362
- if len(var_ids) != 2:
1363
- raise MyError(f"{node[0]}: Expected two terms: `id` and `val-expr`")
1364
-
1365
- inner_env = Env({}, env)
1366
-
1367
- for var, val in node[1]:
1368
- if type(var) is not Sym:
1369
- raise MyError(f"{node[0]}: Expected symbol for `id` term")
1370
- inner_env[var.val] = my_eval(inner_env, val)
1371
-
1372
- for item in node[2:-1]:
1373
- my_eval(inner_env, item)
1374
- return my_eval(inner_env, node[-1])
1375
-
1376
-
1377
- def syn_import(env: Env, node: Node) -> None:
1378
- guard_term(node, 2, 2)
1379
-
1380
- if type(node[1]) is not Sym:
1381
- raise MyError("class name must be an identifier")
1382
-
1383
- module = node[1].val
1384
- error = MyError(f"No module named `{module}`")
1385
-
1386
- if module != "math":
1387
- raise error
1388
- try:
1389
- obj = __import__("auto_editor.lang.libmath", fromlist=["lang"])
1390
- except ImportError:
1391
- raise error
1392
-
1393
- env.update(obj.all())
1394
-
1395
-
1396
- def syn_class(env: Env, node: Node) -> None:
1397
- if len(node) < 2:
1398
- raise MyError(f"{node[0]}: Expects at least 1 term")
1399
-
1400
- if type(node[1]) is not Sym:
1401
- raise MyError("class name must be an identifier")
1402
-
1403
- attr_len = len(node) - 2
1404
- attrs: Any = [None] * (attr_len * 2)
1405
- contracts = [None] * attr_len
1406
-
1407
- for i, item in enumerate(node[2:]):
1408
- if type(item) is not tuple or len(item) != 2:
1409
- raise MyError(f"{node[0]}: Invalid syntax")
1410
-
1411
- contracts[i] = my_eval(env, item[1])
1412
- attrs[i * 2] = item[0].val
1413
- attrs[i * 2 + 1] = contracts[i]
1414
-
1415
- name = node[1].val
1416
- pred = name + "?"
1417
- attrs = tuple(attrs)
1418
-
1419
- env[name] = Proc(
1420
- name,
1421
- lambda *args: PaletClass(name, attrs, list(args)),
1422
- (attr_len, attr_len),
1423
- *contracts,
1424
- )
1425
- env[pred] = Proc(
1426
- pred,
1427
- lambda v: type(v) is PaletClass and v.name == name and v.attrs == attrs,
1428
- (1, 1),
1429
- )
1430
-
1431
-
1432
- def attr(env: Env, node: Node) -> Any:
1433
- guard_term(node, 3, 3)
1434
-
1435
- if type(node[2]) is not Sym:
1436
- raise MyError("@r: attribute must be an identifier")
1437
-
1438
- base = my_eval(env, node[1])
1439
- if type(base) is PaletClass:
1440
- if type(name := node[2]) is not Sym:
1441
- raise MyError("@r: class attribute must be an identifier")
515
+ is_iterable = Contract(
516
+ "iterable?",
517
+ lambda v: type(v) in (str, range, list, tuple, dict, Quoted)
518
+ or isinstance(v, np.ndarray),
519
+ )
520
+ is_boolarr = Contract(
521
+ "bool-array?",
522
+ lambda v: isinstance(v, np.ndarray) and v.dtype.kind == "b",
523
+ )
1442
524
 
1443
- for i, item in enumerate(base.attrs[0::2]):
1444
- if name.val == item:
1445
- return base.values[i]
1446
525
 
1447
- return my_eval(env, (node[2], node[1]))
526
+ def raise_(msg: str | Exception) -> NoReturn:
527
+ raise MyError(msg)
1448
528
 
1449
529
 
1450
530
  def edit_none() -> np.ndarray:
@@ -1554,16 +634,41 @@ def edit_subtitle(pattern, stream=0, **kwargs):
1554
634
  return raise_(e) if levels.strict else levels.all()
1555
635
 
1556
636
 
637
+ class StackTraceManager:
638
+ def __init__(self) -> None:
639
+ self.stack: list[Sym] = []
640
+
641
+ def push(self, sym: Sym) -> None:
642
+ self.stack.append(sym)
643
+
644
+ def pop(self) -> None:
645
+ if self.stack:
646
+ self.stack.pop()
647
+
648
+ def get_stacktrace(self) -> str:
649
+ return "\n".join(
650
+ f" at {sym.val} ({sym.lineno}:{sym.column})"
651
+ for sym in reversed(self.stack)
652
+ )
653
+
654
+
655
+ stack_trace_manager = StackTraceManager()
656
+
657
+
1557
658
  def my_eval(env: Env, node: object) -> Any:
659
+ def make_trace(sym: Sym) -> str:
660
+ return f" at {sym.val} ({sym.lineno}:{sym.column})"
661
+
1558
662
  if type(node) is Sym:
1559
663
  val = env.get(node.val)
1560
664
  if type(val) is NotFound:
665
+ stacktrace = make_trace(node)
1561
666
  if mat := get_close_matches(node.val, env.data):
1562
667
  raise MyError(
1563
- f"variable `{node.val}` not found. Did you mean: {mat[0]}"
668
+ f"variable `{node.val}` not found. Did you mean: {mat[0]}\n{stacktrace}"
1564
669
  )
1565
670
  raise MyError(
1566
- f"variable `{node.val}` not found. Did you mean a string literal."
671
+ f"variable `{node.val}` not found. Did you mean a string literal.\n{stacktrace}"
1567
672
  )
1568
673
  return val
1569
674
 
@@ -1575,44 +680,56 @@ def my_eval(env: Env, node: object) -> Any:
1575
680
  raise MyError("Illegal () expression")
1576
681
 
1577
682
  oper = my_eval(env, node[0])
1578
- if not callable(oper):
1579
- """
1580
- ...No one wants to write (aref a x y) when they could write a[x,y].
1581
- In this particular case there is a way to finesse our way out of the
1582
- problem. If we treat data structures as if they were functions on indexes,
1583
- we could write (a x y) instead, which is even shorter than the Perl form.
1584
- """
1585
- if is_iterable(oper):
1586
- length = len(node[1:])
1587
- if length > 3:
1588
- raise MyError(f"{print_str(node[0])}: slice expects 1 argument")
1589
- if length in (2, 3):
1590
- return p_slice(oper, *(my_eval(env, c) for c in node[1:]))
1591
- if length == 1:
1592
- return ref(oper, my_eval(env, node[1]))
683
+ if isinstance(node[0], Sym):
684
+ stack_trace_manager.push(node[0])
1593
685
 
1594
- raise MyError(
1595
- f"{print_str(oper)} is not a function. Tried to run with args: {print_str(node[1:])}"
1596
- )
686
+ try:
687
+ if not callable(oper):
688
+ """
689
+ ...No one wants to write (aref a x y) when they could write a[x,y].
690
+ In this particular case there is a way to finesse our way out of the
691
+ problem. If we treat data structures as if they were functions on indexes,
692
+ we could write (a x y) instead, which is even shorter than the Perl form.
693
+ """
694
+ if is_iterable(oper):
695
+ length = len(node[1:])
696
+ if length > 3:
697
+ raise MyError(f"{print_str(node[0])}: slice expects 1 argument")
698
+ if length in (2, 3):
699
+ return p_slice(oper, *(my_eval(env, c) for c in node[1:]))
700
+ if length == 1:
701
+ return ref(oper, my_eval(env, node[1]))
1597
702
 
1598
- if type(oper) is Syntax:
1599
- return oper(env, node)
703
+ raise MyError(
704
+ f"{print_str(oper)} is not a function. Tried to run with args: {print_str(node[1:])}"
705
+ )
1600
706
 
1601
- i = 1
1602
- args: list[Any] = []
1603
- kwargs: dict[str, Any] = {}
1604
- while i < len(node):
1605
- result = my_eval(env, node[i])
1606
- if type(result) is Keyword:
707
+ if type(oper) is Syntax:
708
+ return oper(env, node)
709
+
710
+ i = 1
711
+ args: list[Any] = []
712
+ kwargs: dict[str, Any] = {}
713
+ while i < len(node):
714
+ result = my_eval(env, node[i])
715
+ if type(result) is Keyword:
716
+ i += 1
717
+ if i >= len(node):
718
+ raise MyError("Keyword need argument")
719
+ kwargs[result.val] = my_eval(env, node[i])
720
+ else:
721
+ args.append(result)
1607
722
  i += 1
1608
- if i >= len(node):
1609
- raise MyError("Keyword need argument")
1610
- kwargs[result.val] = my_eval(env, node[i])
1611
- else:
1612
- args.append(result)
1613
- i += 1
1614
723
 
1615
- return oper(*args, **kwargs)
724
+ return oper(*args, **kwargs)
725
+ except MyError as e:
726
+ error_msg = str(e)
727
+ if not error_msg.endswith(make_trace(node[0])):
728
+ error_msg += f"\n{make_trace(node[0])}"
729
+ raise MyError(error_msg)
730
+ finally:
731
+ if isinstance(node[0], Sym):
732
+ stack_trace_manager.pop()
1616
733
 
1617
734
  return node
1618
735
 
@@ -1620,11 +737,6 @@ def my_eval(env: Env, node: object) -> Any:
1620
737
  # fmt: off
1621
738
  env = Env({})
1622
739
  env.update({
1623
- # constants
1624
- "true": True,
1625
- "false": False,
1626
- "all": Sym("all"),
1627
- # edit procedures
1628
740
  "none": Proc("none", edit_none, (0, 0)),
1629
741
  "all/e": Proc("all/e", edit_all, (0, 0)),
1630
742
  "audio-levels": Proc("audio-levels", audio_levels, (1, 1), is_nat),
@@ -1641,218 +753,6 @@ env.update({
1641
753
  is_str, is_nat, is_bool, orc(is_nat, is_void),
1642
754
  {"pattern": 0, "stream": 1, "ignore-case": 2, "max-count": 3}
1643
755
  ),
1644
- # syntax
1645
- "lambda": Syntax(syn_lambda),
1646
- "λ": Syntax(syn_lambda),
1647
- "define": Syntax(syn_define),
1648
- "define/c": Syntax(syn_definec),
1649
- "set!": Syntax(syn_set),
1650
- "incf": Syntax(syn_incf),
1651
- "decf": Syntax(syn_decf),
1652
- "&=": Syntax(syn_strappend),
1653
- "quote": Syntax(syn_quote),
1654
- "if": Syntax(syn_if),
1655
- "when": Syntax(syn_when),
1656
- "cond": Syntax(syn_cond),
1657
- "case": Syntax(syn_case),
1658
- "let": Syntax(syn_let),
1659
- "let*": Syntax(syn_let_star),
1660
- "import": Syntax(syn_import),
1661
- "class": Syntax(syn_class),
1662
- "@r": Syntax(attr),
1663
- # loops
1664
- "for": Syntax(syn_for),
1665
- "for-items": Syntax(syn_for_items),
1666
- # contracts
1667
- "number?": is_num,
1668
- "real?": is_real,
1669
- "int?": is_int,
1670
- "float?": is_float,
1671
- "frac?": is_frac,
1672
- "complex?": Contract("complex?", lambda v: type(v) is complex),
1673
- "nat?": is_nat,
1674
- "nat1?": is_nat1,
1675
- "threshold?": is_threshold,
1676
- "any": any_p,
1677
- "bool?": is_bool,
1678
- "void?": is_void,
1679
- "symbol?": (is_symbol := Contract("symbol?", lambda v: type(v) is Sym)),
1680
- "string?": is_str,
1681
- "char?": (is_char := Contract("char?", lambda v: type(v) is Char)),
1682
- "list?": (is_list := Contract("list?", lambda v: type(v) is Quoted or type(v) is tuple)),
1683
- "vector?": (is_vector := Contract("vector?", lambda v: type(v) is list)),
1684
- "array?": (is_array := Contract("array?", lambda v: isinstance(v, np.ndarray))),
1685
- "bool-array?": is_boolarr,
1686
- "range?": (is_range := Contract("range?", lambda v: type(v) is range)),
1687
- "iterable?": is_iterable,
1688
- "sequence?": is_sequence,
1689
- "procedure?": is_proc,
1690
- "contract?": is_cont,
1691
- "hash?": (is_hash := Contract("hash?", lambda v: isinstance(v, dict))),
1692
- "begin": Proc("begin", lambda *x: x[-1] if x else None, (0, None)),
1693
- "void": Proc("void", lambda *v: None, (0, 0)),
1694
- # control / b-arrays
1695
- "not": Proc("not", lambda v: not v if type(v) is bool else logical_not(v), (1, 1), bool_or_barr),
1696
- "and": Syntax(syn_and),
1697
- "or": Syntax(syn_or),
1698
- "xor": Proc("xor", _xor, (2, None), bool_or_barr),
1699
- # booleans
1700
- ">": Proc(">", gt, (2, 2), is_real),
1701
- ">=": Proc(">=", ge, (2, 2), is_real),
1702
- "<": Proc("<", lt, (2, 2), is_real),
1703
- "<=": Proc("<=", le, (2, 2), is_real),
1704
- "=": Proc("=", equal_num, (1, None), is_num),
1705
- "eq?": Proc("eq?", is_, (2, 2)),
1706
- "equal?": Proc("equal?", is_equal, (2, 2)),
1707
- "zero?": UserProc(env, "zero?", ["z"], (is_num,), ((Sym("="), Sym("z"), 0),)),
1708
- "positive?": UserProc(env, "positive?", ["x"], (is_real,), ((Sym(">"), Sym("x"), 0),)),
1709
- "negative?": UserProc(env, "negative?", ["x"], (is_real,), ((Sym("<"), Sym("x"), 0),)),
1710
- "even?": UserProc(
1711
- env, "even?", ["n"], (is_int,), ((Sym("zero?"), (Sym("mod"), Sym("n"), 2)),)),
1712
- "odd?": UserProc(
1713
- env, "odd?", ["n"], (is_int,), ((Sym("not"), (Sym("even?"), Sym("n"))),)),
1714
- ">=/c": Proc(">=/c", gte_c, (1, 1), is_real),
1715
- ">/c": Proc(">/c", gt_c, (1, 1), is_real),
1716
- "<=/c": Proc("<=/c", lte_c, (1, 1), is_real),
1717
- "</c": Proc("</c", lt_c, (1, 1), is_real),
1718
- "between/c": Proc("between/c", between_c, (2, 2), is_real),
1719
- # numbers
1720
- "+": Proc("+", lambda *v: sum(v), (0, None), is_num),
1721
- "-": Proc("-", minus, (1, None), is_num),
1722
- "*": Proc("*", lambda *v: reduce(mul, v, 1), (0, None), is_num),
1723
- "/": Proc("/", num_div, (1, None), is_num),
1724
- "div": Proc("div", int_div, (2, None), is_int),
1725
- "add1": Proc("add1", lambda z: z + 1, (1, 1), is_num),
1726
- "sub1": Proc("sub1", lambda z: z - 1, (1, 1), is_num),
1727
- "sqrt": Proc("sqrt", _sqrt, (1, 1), is_num),
1728
- "real-part": Proc("real-part", lambda v: v.real, (1, 1), is_num),
1729
- "imag-part": Proc("imag-part", lambda v: v.imag, (1, 1), is_num),
1730
- # reals
1731
- "pow": Proc("pow", pow, (2, 2), is_real),
1732
- "abs": Proc("abs", abs, (1, 1), is_real),
1733
- "round": Proc("round", round, (1, 1), is_real),
1734
- "max": Proc("max", lambda *v: max(v), (1, None), is_real),
1735
- "min": Proc("min", lambda *v: min(v), (1, None), is_real),
1736
- "max-seq": Proc("max-seq", max, (1, 1), is_sequence),
1737
- "min-seq": Proc("min-seq", min, (1, 1), is_sequence),
1738
- "mod": Proc("mod", mod, (2, 2), is_int),
1739
- "modulo": Proc("modulo", mod, (2, 2), is_int),
1740
- # symbols
1741
- "symbol->string": Proc("symbol->string", str, (1, 1), is_symbol),
1742
- "string->symbol": Proc("string->symbol", Sym, (1, 1), is_str),
1743
- # strings
1744
- "string": Proc("string", lambda *v: reduce(add, v, ""), (0, None), is_char),
1745
- "&": Proc("&", lambda *v: reduce(add, v, ""), (0, None), is_str),
1746
- "split": Proc("split", str.split, (1, 2), is_str, is_str),
1747
- "strip": Proc("strip", str.strip, (1, 1), is_str),
1748
- "str-repeat": Proc("str-repeat", mul, (2, 2), is_str, is_int),
1749
- "startswith": Proc("startswith", str.startswith, (2, 2), is_str),
1750
- "endswith": Proc("endswith", str.endswith, (2, 2), is_str),
1751
- "replace": Proc("replace", str.replace, (3, 4), is_str, is_str, is_str, is_int),
1752
- "title": Proc("title", str.title, (1, 1), is_str),
1753
- "lower": Proc("lower", str.lower, (1, 1), is_str),
1754
- "upper": Proc("upper", str.upper, (1, 1), is_str),
1755
- "join": Proc("join", palet_join, (2, 2), is_vector, is_str),
1756
- # format
1757
- "char->int": Proc("char->int", lambda c: ord(c.val), (1, 1), is_char),
1758
- "int->char": Proc("int->char", Char, (1, 1), is_int),
1759
- "~a": Proc("~a", lambda *v: "".join([display_str(a) for a in v]), (0, None)),
1760
- "~s": Proc("~s", lambda *v: " ".join([display_str(a) for a in v]), (0, None)),
1761
- "~v": Proc("~v", lambda *v: " ".join([print_str(a) for a in v]), (0, None)),
1762
- # keyword
1763
- "keyword?": is_keyw,
1764
- "keyword->string": Proc("keyword->string", lambda v: v.val.val, (1, 1), is_keyw),
1765
- "string->keyword": Proc("string->keyword", QuotedKeyword, (1, 1), is_str),
1766
- # lists
1767
- "list": Proc("list", lambda *a: Quoted(a), (0, None)),
1768
- "append": Proc("append", list_append, (0, None), is_list),
1769
- # vectors
1770
- "vector": Proc("vector", lambda *a: list(a), (0, None)),
1771
- "make-vector": Proc(
1772
- "make-vector", lambda size, a=0: [a] * size, (1, 2), is_nat, any_p
1773
- ),
1774
- "add!": Proc("add!", list.append, (2, 2), is_vector, any_p),
1775
- "pop!": Proc("pop!", list.pop, (1, 1), is_vector),
1776
- "vec-set!": Proc("vec-set!", vector_set, (3, 3), is_vector, is_int, any_p),
1777
- "vec-append": Proc("vec-append", lambda *v: reduce(add, v, []), (0, None), is_vector),
1778
- "vec-extend!": Proc("vec-extend!", vector_extend, (2, None), is_vector),
1779
- "sort": Proc("sort", sorted, (1, 1), is_vector),
1780
- "sort!": Proc("sort!", list.sort, (1, 1), is_vector),
1781
- # arrays
1782
- "array": Proc("array", array_proc, (2, None), is_symbol, is_real),
1783
- "make-array": Proc("make-array", make_array, (2, 3), is_symbol, is_nat, is_real),
1784
- "array-splice!": Proc(
1785
- "array-splice!", splice, (2, 4), is_array, is_real, is_int, is_int
1786
- ),
1787
- "array-copy": Proc("array-copy", np.copy, (1, 1), is_array),
1788
- "count-nonzero": Proc("count-nonzero", np.count_nonzero, (1, 1), is_array),
1789
- # bool arrays
1790
- "bool-array": Proc(
1791
- "bool-array", lambda *a: np.array(a, dtype=np.bool_), (1, None), is_nat
1792
- ),
1793
- "margin": Proc("margin", margin, (2, 3), is_boolarr, is_int),
1794
- "mincut": Proc("mincut", mincut, (2, 2), is_boolarr, is_nat),
1795
- "minclip": Proc("minclip", minclip, (2, 2), is_boolarr, is_nat),
1796
- "maxcut": Proc("maxcut", maxcut, (2, 2), is_boolarr, is_nat),
1797
- "maxclip": Proc("maxclip", maxclip, (2, 2), is_boolarr, is_nat),
1798
- # ranges
1799
- "range": Proc("range", range, (1, 3), is_int, is_int, int_not_zero),
1800
- # generic iterables
1801
- "len": Proc("len", len, (1, 1), is_iterable),
1802
- "reverse": Proc("reverse", lambda v: v[::-1], (1, 1), is_sequence),
1803
- "ref": Proc("ref", ref, (2, 2), is_sequence, is_int),
1804
- "slice": Proc("slice", p_slice, (2, 4), is_sequence, is_int),
1805
- # procedures
1806
- "map": Proc("map", palet_map, (2, 2), is_proc, is_sequence),
1807
- "apply": Proc("apply", lambda p, s: p(*s), (2, 2), is_proc, is_sequence),
1808
- "and/c": Proc("and/c", andc, (1, None), is_cont),
1809
- "or/c": Proc("or/c", orc, (1, None), is_cont),
1810
- "not/c": Proc("not/c", notc, (1, 1), is_cont),
1811
- # hashs
1812
- "hash": Proc("hash", palet_hash, (0, None)),
1813
- "hash-ref": Proc("hash", hash_ref, (2, 2), is_hash, any_p),
1814
- "hash-set!": Proc("hash-set!", hash_set, (3, 3), is_hash, any_p, any_p),
1815
- "has-key?": Proc("has-key?", lambda h, k: k in h, (2, 2), is_hash, any_p),
1816
- "hash-remove!": Proc("hash-remove!", hash_remove, (2, 2), is_hash, any_p),
1817
- "hash-update!": UserProc(env, "hash-update!", ["h", "v", "up"], (is_hash, any_p),
1818
- (
1819
- (Sym("hash-set!"), Sym("h"), Sym("v"), (Sym("up"), (Sym("h"), Sym("v"))),),
1820
- ),
1821
- ),
1822
- # i/o
1823
- "open-output-file": Proc("open-output-file", initOutPort, (1, 1), is_str),
1824
- "output-port?": (op := Contract("output-port?", lambda v: type(v) is OutputPort)),
1825
- "close-port": Proc("close-port", OutputPort.close, (1, 1), op),
1826
- "closed?": Proc("closed?", lambda o: o.closed, (1, 1), op),
1827
- # printing
1828
- "display": Proc("display",
1829
- lambda v, f=None: print(display_str(v), end="", file=f), (1, 2), any_p, op),
1830
- "displayln": Proc("displayln",
1831
- lambda v, f=None: print(display_str(v), file=f), (1, 2), any_p, op),
1832
- "print": Proc("print",
1833
- lambda v, f=None: print(print_str(v), end="", file=f), (1, 2), any_p, op),
1834
- "println": Proc("println",
1835
- lambda v, f=None: print(print_str(v), file=f), (1, 2), any_p, op),
1836
- # actions
1837
- "assert": Proc("assert", palet_assert, (1, 2), any_p, orc(is_str, False)),
1838
- "error": Proc("error", raise_, (1, 1), is_str),
1839
- "sleep": Proc("sleep", sleep, (1, 1), is_int_or_float),
1840
- "system": Proc("system", palet_system, (1, 1), is_str),
1841
- # conversions
1842
- "number->string": Proc("number->string", number_to_string, (1, 1), is_num),
1843
- "string->vector": Proc(
1844
- "string->vector", lambda s: [Char(c) for c in s], (1, 1), is_str
1845
- ),
1846
- "range->vector": Proc("range->vector", list, (1, 1), is_range),
1847
- # reflexion
1848
- "var-exists?": Proc("var-exists?", lambda sym: sym.val in env, (1, 1), is_symbol),
1849
- "rename": Syntax(syn_rename),
1850
- "delete": Syntax(syn_delete),
1851
- "eval": Proc("eval",
1852
- lambda tl: my_eval(env, tl.val) if type(tl) is Quoted
1853
- else (my_eval(env, tl) if type(tl) is Sym else tl),
1854
- (1, 1)
1855
- ),
1856
756
  })
1857
757
  # fmt: on
1858
758