auto-editor 23.35.1__py3-none-any.whl → 23.40.1__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
@@ -8,7 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  import cmath
10
10
  import math
11
- import random
11
+ from dataclasses import dataclass
12
12
  from difflib import get_close_matches
13
13
  from fractions import Fraction
14
14
  from functools import reduce
@@ -17,6 +17,7 @@ from time import sleep
17
17
  from typing import TYPE_CHECKING
18
18
 
19
19
  import numpy as np
20
+ from numpy import logical_and, logical_not, logical_or, logical_xor
20
21
 
21
22
  from auto_editor.analyze import edit_method, mut_remove_large, mut_remove_small
22
23
  from auto_editor.lib.contracts import *
@@ -26,7 +27,7 @@ from auto_editor.utils.func import boolop, mut_margin
26
27
 
27
28
  if TYPE_CHECKING:
28
29
  from collections.abc import Callable
29
- from typing import Any, NoReturn
30
+ from typing import Any, Literal, NoReturn
30
31
 
31
32
  from numpy.typing import NDArray
32
33
 
@@ -45,11 +46,10 @@ class ClosingError(MyError):
45
46
  # #
46
47
  ###############################################################################
47
48
 
48
- SEC_UNITS = ("s", "sec", "secs", "second", "seconds")
49
- VAL, QUOTE, SEC, DB, PER, DOT = "VAL", "QUOTE", "SEC", "DB", "PER", "DOT"
50
49
  LPAREN, RPAREN, LBRAC, RBRAC, LCUR, RCUR, EOF = "(", ")", "[", "]", "{", "}", "EOF"
51
- VLIT = "VLIT"
52
- METHODS = ("audio:", "motion:", "pixeldiff:", "subtitle:", "none:", "all/e:")
50
+ VAL, QUOTE, SEC, DB, DOT, VLIT = "VAL", "QUOTE", "SEC", "DB", "DOT", "VLIT"
51
+ SEC_UNITS = ("s", "sec", "secs", "second", "seconds")
52
+ METHODS = ("audio:", "motion:", "pixeldiff:", "subtitle:")
53
53
  brac_pairs = {LPAREN: RPAREN, LBRAC: RBRAC, LCUR: RCUR}
54
54
 
55
55
  str_escape = {
@@ -65,17 +65,10 @@ str_escape = {
65
65
  }
66
66
 
67
67
 
68
+ @dataclass(slots=True)
68
69
  class Token:
69
- __slots__ = ("type", "value")
70
-
71
- def __init__(self, type: str, value: Any):
72
- self.type = type
73
- self.value = value
74
-
75
- def __str__(self) -> str:
76
- return f"(Token {print_str(self.type)} {print_str(self.value)})"
77
-
78
- __repr__ = __str__
70
+ type: str
71
+ value: Any
79
72
 
80
73
 
81
74
  class Lexer:
@@ -171,19 +164,16 @@ class Lexer:
171
164
  token = SEC
172
165
  elif unit == "dB":
173
166
  token = DB
174
- elif unit == "%":
175
- token = PER
176
- elif unit != "i":
167
+ elif unit != "i" and unit != "%":
177
168
  return Token(VAL, Sym(result + unit))
178
169
 
179
170
  try:
180
171
  if unit == "i":
181
172
  return Token(VAL, complex(result + "j"))
173
+ elif unit == "%":
174
+ return Token(VAL, float(result) / 100)
182
175
  elif "/" in result:
183
- val = Fraction(result)
184
- if val.denominator == 1:
185
- return Token(token, val.numerator)
186
- return Token(token, val)
176
+ return Token(token, Fraction(result))
187
177
  elif "." in result:
188
178
  return Token(token, float(result))
189
179
  else:
@@ -201,6 +191,16 @@ class Lexer:
201
191
  self.advance()
202
192
  return Token(VAL, Char(char))
203
193
 
194
+ if self.char == ":":
195
+ self.advance()
196
+ buf = StringIO()
197
+ while self.char_is_norm():
198
+ assert self.char is not None
199
+ buf.write(self.char)
200
+ self.advance()
201
+
202
+ return Token(VAL, Keyword(buf.getvalue()))
203
+
204
204
  if self.char is not None and self.char in "([{":
205
205
  brac_type = self.char
206
206
  self.advance()
@@ -368,14 +368,12 @@ class Lexer:
368
368
  ###############################################################################
369
369
 
370
370
 
371
+ @dataclass(slots=True)
371
372
  class Method:
372
- __slots__ = "val"
373
-
374
- def __init__(self, val: str):
375
- self.val = val
373
+ val: str
376
374
 
377
375
  def __str__(self) -> str:
378
- return f'(Method "{self.val}")'
376
+ return f"#<method:{self.val}>"
379
377
 
380
378
  __repr__ = __str__
381
379
 
@@ -405,6 +403,7 @@ class Parser:
405
403
  self.eat()
406
404
  return [list, lit_arr]
407
405
 
406
+ # Handle unhygienic macros in next four cases
408
407
  if token.type == SEC:
409
408
  self.eat()
410
409
  return [Sym("round"), [Sym("*"), token.value, Sym("timebase")]]
@@ -413,10 +412,6 @@ class Parser:
413
412
  self.eat()
414
413
  return [Sym("pow"), 10, [Sym("/"), token.value, 20]]
415
414
 
416
- if token.type == PER:
417
- self.eat()
418
- return [Sym("/"), token.value, 100.0]
419
-
420
415
  if token.type == DOT:
421
416
  self.eat()
422
417
  if type(token.value[1].value) is not Sym:
@@ -463,43 +458,15 @@ class Parser:
463
458
  ###############################################################################
464
459
 
465
460
 
466
- def check_args(
467
- o: str,
468
- values: list | tuple,
469
- arity: tuple[int, int | None],
470
- cont: list[Contract] | None,
471
- ) -> None:
472
- lower, upper = arity
473
- amount = len(values)
474
- if upper is not None and lower > upper:
475
- raise ValueError("lower must be less than upper")
476
- if lower == upper and len(values) != lower:
477
- raise MyError(f"{o}: Arity mismatch. Expected {lower}, got {amount}")
478
-
479
- if upper is None and amount < lower:
480
- raise MyError(f"{o}: Arity mismatch. Expected at least {lower}, got {amount}")
481
- if upper is not None and (amount > upper or amount < lower):
482
- raise MyError(
483
- f"{o}: Arity mismatch. Expected between {lower} and {upper}, got {amount}"
484
- )
485
-
486
- if cont is None:
487
- return
488
-
489
- for i, val in enumerate(values):
490
- check = cont[-1] if i >= len(cont) else cont[i]
491
- if not check_contract(check, val):
492
- raise MyError(f"{o} expected a {check.name}, got {print_str(val)}")
493
-
494
-
495
461
  is_cont = Contract("contract?", is_contract)
496
462
  is_iterable = Contract(
497
463
  "iterable?",
498
- lambda v: type(v) in (str, range) or isinstance(v, (list, dict, np.ndarray)),
464
+ lambda v: type(v) in (str, range, Quoted)
465
+ or isinstance(v, (list, dict, np.ndarray)),
499
466
  )
500
467
  is_sequence = Contract(
501
468
  "sequence?",
502
- lambda v: type(v) in (str, range) or isinstance(v, (list, np.ndarray)),
469
+ lambda v: type(v) in (str, range, Quoted) or isinstance(v, (list, np.ndarray)),
503
470
  )
504
471
  is_boolarr = Contract(
505
472
  "bool-array?",
@@ -509,6 +476,32 @@ bool_or_barr = Contract(
509
476
  "(or/c bool? bool-array?)",
510
477
  lambda v: type(v) is bool or is_boolarr(v),
511
478
  )
479
+ is_keyw = Contract("keyword?", lambda v: type(v) is QuotedKeyword)
480
+
481
+
482
+ @dataclass(slots=True)
483
+ class OutputPort:
484
+ name: str
485
+ port: Any
486
+ write: Any
487
+ closed: bool
488
+
489
+ def close(self) -> None:
490
+ if not self.closed:
491
+ self.port.close()
492
+
493
+ def __str__(self) -> str:
494
+ return f"#<output-port:{self.name}>"
495
+
496
+ __repr__ = __str__
497
+
498
+
499
+ def initOutPort(name: str) -> OutputPort | Literal[False]:
500
+ try:
501
+ port = open(name, "w", encoding="utf-8")
502
+ except Exception:
503
+ return False
504
+ return OutputPort(name, port, port.write, False)
512
505
 
513
506
 
514
507
  def raise_(msg: str) -> None:
@@ -544,13 +537,8 @@ def num_div(z: Number, *w: Number) -> Number:
544
537
  if num == 0:
545
538
  raise MyError("/: division by zero")
546
539
 
547
- if type(num) is int:
548
- num = Fraction(num)
549
-
550
540
  z /= num
551
541
 
552
- if type(z) is Fraction and z.denominator == 1:
553
- return z.numerator
554
542
  return z
555
543
 
556
544
 
@@ -572,9 +560,9 @@ def _sqrt(v: Number) -> Number:
572
560
 
573
561
  def _xor(*vals: Any) -> bool | BoolList:
574
562
  if is_boolarr(vals[0]):
575
- check_args("xor", vals, (2, None), [is_boolarr])
576
- return reduce(lambda a, b: boolop(a, b, np.logical_xor), vals)
577
- check_args("xor", vals, (2, None), [is_bool])
563
+ check_args("xor", vals, (2, None), (is_boolarr,))
564
+ return reduce(lambda a, b: boolop(a, b, logical_xor), vals)
565
+ check_args("xor", vals, (2, None), (is_bool,))
578
566
  return reduce(lambda a, b: a ^ b, vals)
579
567
 
580
568
 
@@ -600,6 +588,13 @@ def number_to_string(val: Number) -> str:
600
588
  return f"{val}"
601
589
 
602
590
 
591
+ def palet_join(v: Any, s: str) -> str:
592
+ try:
593
+ return s.join(v)
594
+ except Exception:
595
+ raise MyError("join: expected string?")
596
+
597
+
603
598
  dtype_map = {
604
599
  Sym("bool"): np.bool_,
605
600
  Sym("int8"): np.int8,
@@ -662,11 +657,11 @@ def maxcut(oarr: BoolList, _min: int) -> BoolList:
662
657
 
663
658
  def margin(a: int, b: Any, c: Any = None) -> BoolList:
664
659
  if c is None:
665
- check_args("margin", [a, b], (2, 2), [is_int, is_boolarr])
660
+ check_args("margin", [a, b], (2, 2), (is_int, is_boolarr))
666
661
  oarr = b
667
662
  start, end = a, a
668
663
  else:
669
- check_args("margin", [a, b, c], (3, 3), [is_int, is_int, is_boolarr])
664
+ check_args("margin", [a, b, c], (3, 3), (is_int, is_int, is_boolarr))
670
665
  oarr = c
671
666
  start, end = a, b
672
667
 
@@ -687,28 +682,14 @@ def vector_extend(vec: list, *more_vecs: list) -> None:
687
682
  vec.extend(more)
688
683
 
689
684
 
690
- def randrange(*args: int) -> int:
691
- try:
692
- return random.randrange(*args)
693
- except ValueError:
694
- raise MyError("randrange: got empty range")
695
-
696
-
697
- def palet_map(proc: Proc, seq: str | list | range | NDArray) -> Any:
685
+ def palet_map(proc: Proc, seq: Any) -> Any:
698
686
  if type(seq) is str:
699
- return str(map(proc.proc, seq))
687
+ return str(map(proc, seq))
688
+ if type(seq) is Quoted:
689
+ return Quoted(list(map(proc, seq.val)))
700
690
  if isinstance(seq, (list, range)):
701
- return list(map(proc.proc, seq))
702
-
703
- if isinstance(seq, np.ndarray):
704
- if proc.arity[0] != 0:
705
- raise MyError("map: procedure must take at least one arg")
706
- check_args(proc.name, [0], (1, 1), None)
707
- return proc.proc(seq)
708
-
709
-
710
- def apply(proc: Proc, seq: str | list | range) -> Any:
711
- return reduce(proc.proc, seq)
691
+ return list(map(proc, seq))
692
+ return proc(seq)
712
693
 
713
694
 
714
695
  def ref(seq: Any, ref: int) -> Any:
@@ -791,41 +772,99 @@ def palet_system(cmd: str) -> bool:
791
772
  class UserProc(Proc):
792
773
  """A user-defined procedure."""
793
774
 
794
- __slots__ = ("env", "parms", "body", "name", "arity", "contracts")
775
+ __slots__ = ("env", "name", "parms", "body", "contracts", "arity")
795
776
 
796
777
  def __init__(
797
778
  self,
798
779
  env: Env,
799
780
  name: str,
800
- parms: list,
781
+ parms: list[str],
782
+ contracts: tuple[Any, ...],
801
783
  body: list,
802
- contracts: list[Any] | None = None,
803
- eat_last: bool = False,
804
784
  ):
805
785
  self.env = env
806
- self.parms = [f"{p}" for p in parms]
807
- self.body = body
808
786
  self.name = name
787
+ self.parms = parms
788
+ self.body = body
789
+ self.contracts = contracts
809
790
 
810
- if eat_last:
791
+ if parms and parms[-1] == "...":
792
+ parms.pop()
811
793
  self.arity: tuple[int, int | None] = len(parms) - 1, None
812
794
  else:
813
795
  self.arity = len(parms), len(parms)
814
796
 
815
- self.contracts = contracts
816
-
817
797
  def __call__(self, *args: Any) -> Any:
798
+ check_args(self.name, args, self.arity, self.contracts)
799
+
818
800
  if self.arity[1] is None:
819
- largs = list(args)
820
- args = tuple([largs[len(self.parms) - 1 :]])
801
+ args = tuple(
802
+ list(args[: len(self.parms) - 1]) + [list(args[len(self.parms) - 1 :])]
803
+ )
821
804
 
822
805
  inner_env = Env(dict(zip(self.parms, args)), self.env)
823
806
 
824
807
  for item in self.body[0:-1]:
825
808
  my_eval(inner_env, item)
826
- result = my_eval(inner_env, self.body[-1])
827
809
 
828
- return result
810
+ return my_eval(inner_env, self.body[-1])
811
+
812
+
813
+ @dataclass(slots=True)
814
+ class KeywordProc:
815
+ env: Env
816
+ name: str
817
+ parms: list[str]
818
+ kw_parms: list[str]
819
+ body: list
820
+ arity: tuple[int, None]
821
+ contracts: list[Any] | None = None
822
+
823
+ def __call__(self, *args: Any) -> Any:
824
+ env = {}
825
+
826
+ for i, parm in enumerate(self.parms):
827
+ if type(args[i]) is Keyword:
828
+ raise MyError(f"Invalid keyword `{args[i]}`")
829
+ env[parm] = args[i]
830
+
831
+ remain_args = args[len(self.parms) :]
832
+
833
+ allow_pos = True
834
+ pos_index = 0
835
+ key = ""
836
+ for arg in remain_args:
837
+ if type(arg) is Keyword:
838
+ if key:
839
+ raise MyError("Expected value for keyword but got another keyword")
840
+ key = arg.val
841
+ allow_pos = False
842
+ elif key:
843
+ env[key] = arg
844
+ key = ""
845
+ else:
846
+ if not allow_pos:
847
+ raise MyError("Positional argument not allowed here")
848
+ if pos_index >= len(self.kw_parms):
849
+ base = f"`{self.name}` has an arity mismatch. Expected"
850
+ upper = len(self.parms) + len(self.kw_parms)
851
+ raise MyError(f"{base} at most {upper}")
852
+
853
+ env[self.kw_parms[pos_index]] = arg
854
+ pos_index += 1
855
+
856
+ inner_env = Env(env, self.env)
857
+
858
+ for item in self.body[0:-1]:
859
+ my_eval(inner_env, item)
860
+
861
+ return my_eval(inner_env, self.body[-1])
862
+
863
+ def __str__(self) -> str:
864
+ return self.name
865
+
866
+ def __repr__(self) -> str:
867
+ return f"#<kw-proc:{self.name}>"
829
868
 
830
869
 
831
870
  class Syntax:
@@ -868,62 +907,97 @@ def check_for_syntax(env: Env, node: list) -> tuple[Sym, Any]:
868
907
 
869
908
 
870
909
  def syn_lambda(env: Env, node: list) -> UserProc:
871
- if not isinstance(node[1], list):
910
+ if len(node) < 3:
911
+ raise MyError(f"{node[0]}: too few terms")
912
+
913
+ if type(node[1]) is not list:
872
914
  raise MyError(f"{node[0]}: bad syntax")
873
915
 
874
- return UserProc(env, "", node[1], node[2:]) # parms, body
916
+ parms: list[str] = []
917
+ for item in node[1]:
918
+ if type(item) is not Sym:
919
+ raise MyError(f"{node[0]}: must be an identifier")
920
+
921
+ parms.append(f"{item}")
922
+
923
+ return UserProc(env, "", parms, (), node[2:])
875
924
 
876
925
 
877
926
  def syn_define(env: Env, node: list) -> None:
878
927
  if len(node) < 3:
879
- raise MyError(f"{node[0]}: too few args")
928
+ raise MyError(f"{node[0]}: too few terms")
929
+
930
+ if type(node[1]) is list:
931
+ term = node[1]
932
+ body = node[2:]
880
933
 
881
- if isinstance(node[1], list):
882
- if not node[1] or type(node[1][0]) is not Sym:
934
+ if not term or type(term[0]) is not Sym:
883
935
  raise MyError(f"{node[0]}: proc-binding must be an identifier")
884
936
 
885
- n = node[1][0].val
937
+ n = term[0].val
938
+ parms: list[str] = []
939
+ kparms: list[str] = []
940
+ kw_only = False
941
+
942
+ for item in term[1:]:
943
+ if kw_only:
944
+ if type(item) is Sym:
945
+ raise MyError(f"{node[0]}: {item} must be a keyword")
946
+ if type(item) is not Keyword:
947
+ raise MyError(f"{node[0]}: must be a keyword")
948
+ kparms.append(item.val)
949
+ else:
950
+ if type(item) is Keyword:
951
+ kw_only = True
952
+ kparms.append(item.val)
953
+ elif type(item) is Sym:
954
+ parms.append(item.val)
955
+ else:
956
+ raise MyError(f"{node[0]}: must be an identifier")
886
957
 
887
- eat_last = False
888
- if node[1][1:] and node[1][-1] == Sym("..."):
889
- eat_last = True
890
- parameters = node[1][1:-1]
958
+ if kw_only:
959
+ env[n] = KeywordProc(env, n, parms, kparms, body, (len(parms), None))
891
960
  else:
892
- parameters = node[1][1:]
893
-
894
- body = node[2:]
895
- env[n] = UserProc(env, n, parameters, body, eat_last=eat_last)
961
+ env[n] = UserProc(env, n, parms, (), body)
896
962
  return None
963
+
897
964
  elif type(node[1]) is not Sym:
898
965
  raise MyError(f"{node[0]}: must be an identifier")
899
966
 
900
- n = node[1].val
901
-
902
967
  if len(node) > 3:
903
- raise MyError(f"{node[0]}: bad syntax (multiple expressions after identifier)")
968
+ raise MyError(f"{node[0]}: multiple expressions after identifier")
969
+
970
+ n = node[1].val
904
971
 
905
972
  if (
906
- isinstance(node[2], list)
973
+ type(node[2]) is list
907
974
  and node[2]
908
975
  and type(node[2][0]) is Sym
909
976
  and node[2][0].val in ("lambda", "λ")
910
977
  ):
911
- parameters = node[2][1]
978
+ terms = node[2][1]
912
979
  body = node[2][2:]
913
- env[n] = UserProc(env, n, parameters, body)
980
+
981
+ parms = []
982
+ for item in terms:
983
+ if type(item) is not Sym:
984
+ raise MyError(f"{node[0]}: must be an identifier")
985
+
986
+ parms.append(f"{item}")
987
+
988
+ env[n] = UserProc(env, n, parms, (), body)
989
+
914
990
  else:
915
991
  for item in node[2:-1]:
916
992
  my_eval(env, item)
917
993
  env[n] = my_eval(env, node[-1])
918
994
 
919
- return None
920
-
921
995
 
922
996
  def syn_definec(env: Env, node: list) -> None:
923
997
  if len(node) < 3:
924
- raise MyError(f"{node[0]}: bad syntax")
998
+ raise MyError(f"{node[0]}: too few terms")
925
999
 
926
- if not isinstance(node[1], list):
1000
+ if type(node[1]) is not list:
927
1001
  raise MyError(f"{node[0]} only allows procedure declarations")
928
1002
 
929
1003
  if not node[1] or type(node[1][0]) is not Sym:
@@ -931,22 +1005,24 @@ def syn_definec(env: Env, node: list) -> None:
931
1005
 
932
1006
  n = node[1][0].val
933
1007
 
934
- contracts: list[Proc | Contract] = []
935
- parameters: list[Sym] = []
1008
+ contracts: list[Any] = []
1009
+ parms: list[str] = []
936
1010
  for item in node[1][1:]:
937
- if len(item) != 2:
1011
+ if item == Sym("->"):
1012
+ break
1013
+ if type(item) is not list or len(item) != 2:
938
1014
  raise MyError(f"{node[0]}: bad var-binding syntax")
939
1015
  if type(item[0]) is not Sym:
940
1016
  raise MyError(f"{node[0]}: binding must be identifier")
941
1017
 
942
- parameters.append(item[0])
943
1018
  con = my_eval(env, item[1])
944
1019
  if not is_cont(con):
945
1020
  raise MyError(f"{node[0]}: {print_str(con)} is not a valid contract")
946
1021
 
1022
+ parms.append(f"{item[0]}")
947
1023
  contracts.append(con)
948
1024
 
949
- env[n] = UserProc(env, n, parameters, node[2:], contracts)
1025
+ env[n] = UserProc(env, n, parms, tuple(contracts), node[2:])
950
1026
  return None
951
1027
 
952
1028
 
@@ -1015,6 +1091,23 @@ def syn_decf(env: Env, node: list) -> None:
1015
1091
  env[name] -= 1
1016
1092
 
1017
1093
 
1094
+ def syn_strappend(env: Env, node: list) -> None:
1095
+ guard_term(node, 3, 3)
1096
+
1097
+ if type(node[1]) is not Sym:
1098
+ raise MyError(f"{node[0]}: Expected identifier, got {print_str(node[1])}")
1099
+ name = node[1].val
1100
+
1101
+ if env[name] is None:
1102
+ raise MyError(f"{node[0]}: `{name}` is not defined")
1103
+ if not is_str(env[name]):
1104
+ raise MyError(f"{node[0]}: `{name}` is not a string?")
1105
+
1106
+ if not is_str(num := my_eval(env, node[2])):
1107
+ raise MyError(f"{node[0]}: Expected string? got: {print_str(num)}")
1108
+ env[name] += num
1109
+
1110
+
1018
1111
  def syn_for(env: Env, node: list) -> None:
1019
1112
  var, my_iter = check_for_syntax(env, node)
1020
1113
 
@@ -1030,8 +1123,12 @@ def syn_for(env: Env, node: list) -> None:
1030
1123
  my_eval(env, c)
1031
1124
 
1032
1125
 
1033
- def syn_quote(env: Env, node: list) -> list:
1126
+ def syn_quote(env: Env, node: list) -> Any:
1034
1127
  guard_term(node, 2, 2)
1128
+ if type(node[1]) is Keyword:
1129
+ return QuotedKeyword(node[1])
1130
+ if type(node[1]) is list:
1131
+ return Quoted(node[1])
1035
1132
  return node[1]
1036
1133
 
1037
1134
 
@@ -1082,8 +1179,8 @@ def syn_and(env: Env, node: list) -> Any:
1082
1179
 
1083
1180
  if is_boolarr(first):
1084
1181
  vals = [first] + [my_eval(env, n) for n in node[2:]]
1085
- check_args(node[0], vals, (2, None), [is_boolarr])
1086
- return reduce(lambda a, b: boolop(a, b, np.logical_and), vals)
1182
+ check_args(node[0], vals, (2, None), (is_boolarr,))
1183
+ return reduce(lambda a, b: boolop(a, b, logical_and), vals)
1087
1184
 
1088
1185
  raise MyError(f"{node[0]} expects (or/c bool? bool-array?)")
1089
1186
 
@@ -1106,8 +1203,8 @@ def syn_or(env: Env, node: list) -> Any:
1106
1203
 
1107
1204
  if is_boolarr(first):
1108
1205
  vals = [first] + [my_eval(env, n) for n in node[2:]]
1109
- check_args(node[0], vals, (2, None), [is_boolarr])
1110
- return reduce(lambda a, b: boolop(a, b, np.logical_or), vals)
1206
+ check_args(node[0], vals, (2, None), (is_boolarr,))
1207
+ return reduce(lambda a, b: boolop(a, b, logical_or), vals)
1111
1208
 
1112
1209
  raise MyError(f"{node[0]} expects (or/c bool? bool-array?)")
1113
1210
 
@@ -1224,15 +1321,33 @@ def syn_let_star(env: Env, node: list) -> Any:
1224
1321
  return my_eval(inner_env, node[-1])
1225
1322
 
1226
1323
 
1324
+ def syn_class(env: Env, node: list) -> Any:
1325
+ ...
1326
+
1327
+
1227
1328
  def attr(env: Env, node: list) -> Any:
1228
1329
  guard_term(node, 3, 3)
1229
1330
 
1230
- if not isinstance(node[2], Sym):
1331
+ if type(node[2]) is not Sym:
1231
1332
  raise MyError("@r: attribute must be an identifier")
1232
1333
 
1233
1334
  return my_eval(env, [node[2], node[1]])
1234
1335
 
1235
1336
 
1337
+ def edit_none() -> np.ndarray:
1338
+ if "@levels" not in env:
1339
+ raise MyError("Can't use `none` if there's no input media")
1340
+
1341
+ return env["@levels"].none()
1342
+
1343
+
1344
+ def edit_all() -> np.ndarray:
1345
+ if "@levels" not in env:
1346
+ raise MyError("Can't use `all/e` if there's no input media")
1347
+
1348
+ return env["@levels"].all()
1349
+
1350
+
1236
1351
  def my_eval(env: Env, node: object) -> Any:
1237
1352
  if type(node) is Sym:
1238
1353
  val = env.get(node.val)
@@ -1251,10 +1366,9 @@ def my_eval(env: Env, node: object) -> Any:
1251
1366
  raise MyError("Can't use edit methods if there's no input files")
1252
1367
  return edit_method(node.val, env["@filesetup"], env)
1253
1368
 
1254
- if isinstance(node, list):
1369
+ if type(node) is list:
1255
1370
  if not node:
1256
1371
  raise MyError("Illegal () expression")
1257
-
1258
1372
  if node[0] is list: # Handle vector literal
1259
1373
  return [my_eval(env, item) for item in node[1]]
1260
1374
 
@@ -1280,13 +1394,7 @@ def my_eval(env: Env, node: object) -> Any:
1280
1394
  if type(oper) is Syntax:
1281
1395
  return oper(env, node)
1282
1396
 
1283
- values = [my_eval(env, c) for c in node[1:]]
1284
- if type(oper) is Contract:
1285
- check_args(oper.name, values, (1, 1), None)
1286
- else:
1287
- check_args(oper.name, values, oper.arity, oper.contracts)
1288
-
1289
- return oper(*values)
1397
+ return oper(*(my_eval(env, c) for c in node[1:]))
1290
1398
 
1291
1399
  return node
1292
1400
 
@@ -1298,6 +1406,9 @@ env.update({
1298
1406
  "true": True,
1299
1407
  "false": False,
1300
1408
  "all": Sym("all"),
1409
+ # edit procedures
1410
+ "none": Proc("none", edit_none, (0, 0)),
1411
+ "all/e": Proc("all/e", edit_all, (0, 0)),
1301
1412
  # syntax
1302
1413
  "lambda": Syntax(syn_lambda),
1303
1414
  "λ": Syntax(syn_lambda),
@@ -1306,6 +1417,7 @@ env.update({
1306
1417
  "set!": Syntax(syn_set),
1307
1418
  "incf": Syntax(syn_incf),
1308
1419
  "decf": Syntax(syn_decf),
1420
+ "&=": Syntax(syn_strappend),
1309
1421
  "quote": Syntax(syn_quote),
1310
1422
  "if": Syntax(syn_if),
1311
1423
  "when": Syntax(syn_when),
@@ -1313,6 +1425,7 @@ env.update({
1313
1425
  "case": Syntax(syn_case),
1314
1426
  "let": Syntax(syn_let),
1315
1427
  "let*": Syntax(syn_let_star),
1428
+ #"class": Syntax(syn_class),
1316
1429
  "@r": Syntax(attr),
1317
1430
  # loops
1318
1431
  "for": Syntax(syn_for),
@@ -1320,10 +1433,11 @@ env.update({
1320
1433
  "number?": is_num,
1321
1434
  "real?": is_real,
1322
1435
  "int?": is_int,
1323
- "uint?": is_uint,
1324
- "nat?": is_nat,
1325
1436
  "float?": is_float,
1326
1437
  "frac?": is_frac,
1438
+ "complex?": Contract("complex?", lambda v: type(v) is complex),
1439
+ "nat?": is_nat,
1440
+ "nat1?": is_nat1,
1327
1441
  "threshold?": is_threshold,
1328
1442
  "any": any_p,
1329
1443
  "bool?": is_bool,
@@ -1331,7 +1445,7 @@ env.update({
1331
1445
  "symbol?": (is_symbol := Contract("symbol?", lambda v: type(v) is Sym)),
1332
1446
  "string?": is_str,
1333
1447
  "char?": (is_char := Contract("char?", lambda v: type(v) is Char)),
1334
- "vector?": (is_vector := Contract("vector?", lambda v: isinstance(v, list))),
1448
+ "vector?": (is_vector := Contract("vector?", lambda v: type(v) is list)),
1335
1449
  "array?": (is_array := Contract("array?", lambda v: isinstance(v, np.ndarray))),
1336
1450
  "bool-array?": is_boolarr,
1337
1451
  "range?": (is_range := Contract("range?", lambda v: type(v) is range)),
@@ -1343,157 +1457,162 @@ env.update({
1343
1457
  "begin": Proc("begin", lambda *x: x[-1] if x else None, (0, None)),
1344
1458
  "void": Proc("void", lambda *v: None, (0, 0)),
1345
1459
  # control / b-arrays
1346
- "not": Proc(
1347
- "not",
1348
- lambda v: not v if type(v) is bool else np.logical_not(v),
1349
- (1, 1),
1350
- [bool_or_barr],
1351
- ),
1460
+ "not": Proc("not", lambda v: not v if type(v) is bool else logical_not(v), (1, 1), bool_or_barr),
1352
1461
  "and": Syntax(syn_and),
1353
1462
  "or": Syntax(syn_or),
1354
- "xor": Proc("xor", _xor, (2, None), [bool_or_barr]),
1463
+ "xor": Proc("xor", _xor, (2, None), bool_or_barr),
1355
1464
  # booleans
1356
- ">": Proc(">", lambda a, b: a > b, (2, 2), [is_real, is_real]),
1357
- ">=": Proc(">=", lambda a, b: a >= b, (2, 2), [is_real, is_real]),
1358
- "<": Proc("<", lambda a, b: a < b, (2, 2), [is_real, is_real]),
1359
- "<=": Proc("<=", lambda a, b: a <= b, (2, 2), [is_real, is_real]),
1360
- "=": Proc("=", equal_num, (1, None), [is_num]),
1465
+ ">": Proc(">", lambda a, b: a > b, (2, 2), is_real),
1466
+ ">=": Proc(">=", lambda a, b: a >= b, (2, 2), is_real),
1467
+ "<": Proc("<", lambda a, b: a < b, (2, 2), is_real),
1468
+ "<=": Proc("<=", lambda a, b: a <= b, (2, 2), is_real),
1469
+ "=": Proc("=", equal_num, (1, None), is_num),
1361
1470
  "eq?": Proc("eq?", lambda a, b: a is b, (2, 2)),
1362
1471
  "equal?": Proc("equal?", is_equal, (2, 2)),
1363
- "zero?": UserProc(env, "zero?", ["z"], [[Sym("="), Sym("z"), 0]], [is_num]),
1364
- "positive?": UserProc(
1365
- env, "positive?", ["x"], [[Sym(">"), Sym("x"), 0]], [is_real]
1366
- ),
1367
- "negative?": UserProc(
1368
- env, "negative?", ["x"], [[Sym("<"), Sym("x"), 0]], [is_real]
1369
- ),
1472
+ "zero?": UserProc(env, "zero?", ["z"], (is_num,), [[Sym("="), Sym("z"), 0]]),
1473
+ "positive?": UserProc(env, "positive?", ["x"], (is_real,), [[Sym(">"), Sym("x"), 0]]),
1474
+ "negative?": UserProc(env, "negative?", ["x"], (is_real,), [[Sym("<"), Sym("x"), 0]]),
1370
1475
  "even?": UserProc(
1371
- env, "even?", ["n"], [[Sym("zero?"), [Sym("mod"), Sym("n"), 2]]], [is_int]
1476
+ env, "even?", ["n"], (is_int,), [[Sym("zero?"), [Sym("mod"), Sym("n"), 2]]]
1372
1477
  ),
1373
1478
  "odd?": UserProc(
1374
- env, "odd?", ["n"], [[Sym("not"), [Sym("even?"), Sym("n")]]], [is_int]
1479
+ env, "odd?", ["n"], (is_int,), [[Sym("not"), [Sym("even?"), Sym("n")]]]
1375
1480
  ),
1376
- ">=/c": Proc(">=/c", gte_c, (1, 1), [is_real]),
1377
- ">/c": Proc(">/c", gt_c, (1, 1), [is_real]),
1378
- "<=/c": Proc("<=/c", lte_c, (1, 1), [is_real]),
1379
- "</c": Proc("</c", lt_c, (1, 1), [is_real]),
1380
- "between/c": Proc("between/c", between_c, (2, 2), [is_real, is_real]),
1481
+ ">=/c": Proc(">=/c", gte_c, (1, 1), is_real),
1482
+ ">/c": Proc(">/c", gt_c, (1, 1), is_real),
1483
+ "<=/c": Proc("<=/c", lte_c, (1, 1), is_real),
1484
+ "</c": Proc("</c", lt_c, (1, 1), is_real),
1485
+ "between/c": Proc("between/c", between_c, (2, 2), is_real),
1381
1486
  # numbers
1382
- "+": Proc("+", lambda *v: sum(v), (0, None), [is_num]),
1383
- "-": Proc("-", minus, (1, None), [is_num]),
1384
- "*": Proc("*", mul, (0, None), [is_num]),
1385
- "/": Proc("/", num_div, (1, None), [is_num]),
1386
- "div": Proc("div", int_div, (2, None), [is_int]),
1387
- "add1": Proc("add1", lambda z: z + 1, (1, 1), [is_num]),
1388
- "sub1": Proc("sub1", lambda z: z - 1, (1, 1), [is_num]),
1389
- "sqrt": Proc("sqrt", _sqrt, (1, 1), [is_num]),
1390
- "real-part": Proc("real-part", lambda v: v.real, (1, 1), [is_num]),
1391
- "imag-part": Proc("imag-part", lambda v: v.imag, (1, 1), [is_num]),
1487
+ "+": Proc("+", lambda *v: sum(v), (0, None), is_num),
1488
+ "-": Proc("-", minus, (1, None), is_num),
1489
+ "*": Proc("*", mul, (0, None), is_num),
1490
+ "/": Proc("/", num_div, (1, None), is_num),
1491
+ "div": Proc("div", int_div, (2, None), is_int),
1492
+ "add1": Proc("add1", lambda z: z + 1, (1, 1), is_num),
1493
+ "sub1": Proc("sub1", lambda z: z - 1, (1, 1), is_num),
1494
+ "sqrt": Proc("sqrt", _sqrt, (1, 1), is_num),
1495
+ "real-part": Proc("real-part", lambda v: v.real, (1, 1), is_num),
1496
+ "imag-part": Proc("imag-part", lambda v: v.imag, (1, 1), is_num),
1392
1497
  # reals
1393
- "pow": Proc("pow", pow, (2, 2), [is_real]),
1394
- "exp": Proc("exp", math.exp, (1, 1), [is_real]),
1395
- "abs": Proc("abs", abs, (1, 1), [is_real]),
1396
- "ceil": Proc("ceil", math.ceil, (1, 1), [is_real]),
1397
- "floor": Proc("floor", math.floor, (1, 1), [is_real]),
1398
- "round": Proc("round", round, (1, 1), [is_real]),
1399
- "max": Proc("max", lambda *v: max(v), (1, None), [is_real]),
1400
- "min": Proc("min", lambda *v: min(v), (1, None), [is_real]),
1401
- "sin": Proc("sin", math.sin, (1, 1), [is_real]),
1402
- "cos": Proc("cos", math.cos, (1, 1), [is_real]),
1403
- "log": Proc("log", math.log, (1, 2), [is_real, is_real]),
1404
- "tan": Proc("tan", math.tan, (1, 1), [is_real]),
1405
- "mod": Proc("mod", lambda a, b: a % b, (2, 2), [is_int]),
1406
- "modulo": Proc("modulo", lambda a, b: a % b, (2, 2), [is_int]),
1498
+ "pow": Proc("pow", pow, (2, 2), is_real),
1499
+ "exp": Proc("exp", math.exp, (1, 1), is_real),
1500
+ "abs": Proc("abs", abs, (1, 1), is_real),
1501
+ "ceil": Proc("ceil", math.ceil, (1, 1), is_real),
1502
+ "floor": Proc("floor", math.floor, (1, 1), is_real),
1503
+ "round": Proc("round", round, (1, 1), is_real),
1504
+ "max": Proc("max", lambda *v: max(v), (1, None), is_real),
1505
+ "min": Proc("min", lambda *v: min(v), (1, None), is_real),
1506
+ "sin": Proc("sin", math.sin, (1, 1), is_real),
1507
+ "cos": Proc("cos", math.cos, (1, 1), is_real),
1508
+ "log": Proc("log", math.log, (1, 2), is_real),
1509
+ "tan": Proc("tan", math.tan, (1, 1), is_real),
1510
+ "mod": Proc("mod", lambda a, b: a % b, (2, 2), is_int),
1511
+ "modulo": Proc("modulo", lambda a, b: a % b, (2, 2), is_int),
1407
1512
  # symbols
1408
- "symbol->string": Proc("symbol->string", str, (1, 1), [is_symbol]),
1409
- "string->symbol": Proc("string->symbol", Sym, (1, 1), [is_str]),
1513
+ "symbol->string": Proc("symbol->string", str, (1, 1), is_symbol),
1514
+ "string->symbol": Proc("string->symbol", Sym, (1, 1), is_str),
1410
1515
  # strings
1411
- "string": Proc("string", string_append, (0, None), [is_char]),
1412
- "string-append": Proc("string-append", string_append, (0, None), [is_str]),
1413
- "split": Proc("split", str.split, (1, 2), [is_str, is_str]),
1414
- "strip": Proc("strip", str.strip, (1, 1), [is_str]),
1415
- "str-repeat": Proc("str-repeat", lambda s, a: s * a, (2, 2), [is_str, is_int]),
1416
- "startswith": Proc("startswith", str.startswith, (2, 2), [is_str, is_str]),
1417
- "endswith": Proc("endswith", str.endswith, (2, 2), [is_str, is_str]),
1418
- "replace": Proc("replace", str.replace, (3, 4), [is_str, is_str, is_str, is_int]),
1419
- "title": Proc("title", str.title, (1, 1), [is_str]),
1420
- "lower": Proc("lower", str.lower, (1, 1), [is_str]),
1421
- "upper": Proc("upper", str.upper, (1, 1), [is_str]),
1516
+ "string": Proc("string", string_append, (0, None), is_char),
1517
+ "&": Proc("&", string_append, (0, None), is_str),
1518
+ "split": Proc("split", str.split, (1, 2), is_str, is_str),
1519
+ "strip": Proc("strip", str.strip, (1, 1), is_str),
1520
+ "str-repeat": Proc("str-repeat", lambda s, a: s * a, (2, 2), is_str, is_int),
1521
+ "startswith": Proc("startswith", str.startswith, (2, 2), is_str),
1522
+ "endswith": Proc("endswith", str.endswith, (2, 2), is_str),
1523
+ "replace": Proc("replace", str.replace, (3, 4), is_str, is_str, is_str, is_int),
1524
+ "title": Proc("title", str.title, (1, 1), is_str),
1525
+ "lower": Proc("lower", str.lower, (1, 1), is_str),
1526
+ "upper": Proc("upper", str.upper, (1, 1), is_str),
1527
+ "join": Proc("join", palet_join, (2, 2), is_vector, is_str),
1422
1528
  # format
1423
- "char->int": Proc("char->int", lambda c: ord(c.val), (1, 1), [is_char]),
1424
- "int->char": Proc("int->char", Char, (1, 1), [is_int]),
1529
+ "char->int": Proc("char->int", lambda c: ord(c.val), (1, 1), is_char),
1530
+ "int->char": Proc("int->char", Char, (1, 1), is_int),
1425
1531
  "~a": Proc("~a", lambda *v: "".join([display_str(a) for a in v]), (0, None)),
1426
1532
  "~s": Proc("~s", lambda *v: " ".join([display_str(a) for a in v]), (0, None)),
1427
1533
  "~v": Proc("~v", lambda *v: " ".join([print_str(a) for a in v]), (0, None)),
1534
+ # keyword
1535
+ "keyword?": is_keyw,
1536
+ "keyword->string": Proc("keyword->string", lambda v: v.val.val, (1, 1), is_keyw),
1537
+ "string->keyword": Proc("string->keyword", QuotedKeyword, (1, 1), is_str),
1428
1538
  # vectors
1429
1539
  "vector": Proc("vector", lambda *a: list(a), (0, None)),
1430
1540
  "make-vector": Proc(
1431
- "make-vector", lambda size, a=0: [a] * size, (1, 2), [is_uint, any_p]
1541
+ "make-vector", lambda size, a=0: [a] * size, (1, 2), is_nat, any_p
1432
1542
  ),
1433
- "vector-append": Proc("vector-append", vector_append, (0, None), [is_vector]),
1434
- "vector-pop!": Proc("vector-pop!", list.pop, (1, 1), [is_vector]),
1435
- "vector-add!": Proc("vector-add!", list.append, (2, 2), [is_vector, any_p]),
1436
- "vector-set!": Proc("vector-set!", vector_set, (3, 3), [is_vector, is_int, any_p]),
1437
- "vector-extend!": Proc("vector-extend!", vector_extend, (2, None), [is_vector]),
1438
- "sort": Proc("sort", sorted, (1, 1), [is_vector]),
1439
- "sort!": Proc("sort!", list.sort, (1, 1), [is_vector]),
1543
+ "add!": Proc("add!", list.append, (2, 2), is_vector, any_p),
1544
+ "pop!": Proc("pop!", list.pop, (1, 1), is_vector),
1545
+ "vec-set!": Proc("vec-set!", vector_set, (3, 3), is_vector, is_int, any_p),
1546
+ "vec-append": Proc("vec-append", vector_append, (0, None), is_vector),
1547
+ "vec-extend!": Proc("vec-extend!", vector_extend, (2, None), is_vector),
1548
+ "sort": Proc("sort", sorted, (1, 1), is_vector),
1549
+ "sort!": Proc("sort!", list.sort, (1, 1), is_vector),
1440
1550
  # arrays
1441
- "array": Proc("array", array_proc, (2, None), [is_symbol, is_real]),
1442
- "make-array": Proc("make-array", make_array, (2, 3), [is_symbol, is_uint, is_real]),
1551
+ "array": Proc("array", array_proc, (2, None), is_symbol, is_real),
1552
+ "make-array": Proc("make-array", make_array, (2, 3), is_symbol, is_nat, is_real),
1443
1553
  "array-splice!": Proc(
1444
- "array-splice!", splice, (2, 4), [is_array, is_real, is_int, is_int]
1554
+ "array-splice!", splice, (2, 4), is_array, is_real, is_int, is_int
1445
1555
  ),
1446
- "array-copy": Proc("array-copy", np.copy, (1, 1), [is_array]),
1447
- "count-nonzero": Proc("count-nonzero", np.count_nonzero, (1, 1), [is_array]),
1556
+ "array-copy": Proc("array-copy", np.copy, (1, 1), is_array),
1557
+ "count-nonzero": Proc("count-nonzero", np.count_nonzero, (1, 1), is_array),
1448
1558
  # bool arrays
1449
1559
  "bool-array": Proc(
1450
- "bool-array", lambda *a: np.array(a, dtype=np.bool_), (1, None), [is_uint]
1560
+ "bool-array", lambda *a: np.array(a, dtype=np.bool_), (1, None), is_nat
1451
1561
  ),
1452
- "margin": Proc("margin", margin, (2, 3), None),
1453
- "mincut": Proc("mincut", mincut, (2, 2), [is_boolarr, is_uint]),
1454
- "minclip": Proc("minclip", minclip, (2, 2), [is_boolarr, is_uint]),
1455
- "maxcut": Proc("maxcut", maxcut, (2, 2), [is_boolarr, is_uint]),
1456
- "maxclip": Proc("maxclip", maxclip, (2, 2), [is_boolarr, is_uint]),
1562
+ "margin": Proc("margin", margin, (2, 3)),
1563
+ "mincut": Proc("mincut", mincut, (2, 2), is_boolarr, is_nat),
1564
+ "minclip": Proc("minclip", minclip, (2, 2), is_boolarr, is_nat),
1565
+ "maxcut": Proc("maxcut", maxcut, (2, 2), is_boolarr, is_nat),
1566
+ "maxclip": Proc("maxclip", maxclip, (2, 2), is_boolarr, is_nat),
1457
1567
  # ranges
1458
- "range": Proc("range", range, (1, 3), [is_int, is_int, int_not_zero]),
1568
+ "range": Proc("range", range, (1, 3), is_int, is_int, int_not_zero),
1459
1569
  # generic iterables
1460
- "len": Proc("len", len, (1, 1), [is_iterable]),
1461
- "reverse": Proc("reverse", lambda v: v[::-1], (1, 1), [is_sequence]),
1462
- "ref": Proc("ref", ref, (2, 2), [is_sequence, is_int]),
1463
- "slice": Proc("slice", p_slice, (2, 4), [is_sequence, is_int]),
1570
+ "len": Proc("len", len, (1, 1), is_iterable),
1571
+ "reverse": Proc("reverse", lambda v: v[::-1], (1, 1), is_sequence),
1572
+ "ref": Proc("ref", ref, (2, 2), is_sequence, is_int),
1573
+ "slice": Proc("slice", p_slice, (2, 4), is_sequence, is_int),
1464
1574
  # procedures
1465
- "map": Proc("map", palet_map, (2, 2), [is_proc, is_sequence]),
1466
- "apply": Proc("apply", apply, (2, 2), [is_proc, is_sequence]),
1467
- "and/c": Proc("and/c", andc, (1, None), [is_cont]),
1468
- "or/c": Proc("or/c", orc, (1, None), [is_cont]),
1469
- "not/c": Proc("not/c", notc, (1, 1), [is_cont]),
1575
+ "map": Proc("map", palet_map, (2, 2), is_proc, is_sequence),
1576
+ "apply": Proc("apply", lambda p, s: p(*s), (2, 2), is_proc, is_sequence),
1577
+ "and/c": Proc("and/c", andc, (1, None), is_cont),
1578
+ "or/c": Proc("or/c", orc, (1, None), is_cont),
1579
+ "not/c": Proc("not/c", notc, (1, 1), is_cont),
1470
1580
  # hashs
1471
1581
  "hash": Proc("hash", palet_hash, (0, None)),
1472
- "hash-ref": Proc("hash", hash_ref, (2, 2), [is_hash, any_p]),
1473
- "hash-set!": Proc("hash-set!", hash_set, (3, 3), [is_hash, any_p, any_p]),
1474
- "has-key?": Proc("has-key?", lambda h, k: k in h, (2, 2), [is_hash, any_p]),
1475
- "hash-remove!": Proc("hash-remove!", hash_remove, (2, 2), [is_hash, any_p]),
1476
- "hash-update!": UserProc(env, "hash-update!", ["h", "v", "up"],
1582
+ "hash-ref": Proc("hash", hash_ref, (2, 2), is_hash, any_p),
1583
+ "hash-set!": Proc("hash-set!", hash_set, (3, 3), is_hash, any_p, any_p),
1584
+ "has-key?": Proc("has-key?", lambda h, k: k in h, (2, 2), is_hash, any_p),
1585
+ "hash-remove!": Proc("hash-remove!", hash_remove, (2, 2), is_hash, any_p),
1586
+ "hash-update!": UserProc(env, "hash-update!", ["h", "v", "up"], (is_hash, any_p),
1477
1587
  [[Sym("hash-set!"), Sym("h"), Sym("v"), [Sym("up"), [Sym("hash-ref"), Sym("h"), Sym("v")]]]],
1478
- [is_hash, any_p, any_p],
1479
1588
  ),
1589
+ # i/o
1590
+ "open-output-file": Proc("open-output-file", initOutPort, (1, 1), is_str),
1591
+ "output-port?": (op := Contract("output-port?", lambda v: type(v) is OutputPort)),
1592
+ "close-port": Proc("close-port", OutputPort.close, (1, 1), op),
1593
+ "closed?": Proc("closed?", lambda o: o.closed, (1, 1), op),
1594
+ # printing
1595
+ "display": Proc("display",
1596
+ lambda v, f=None: print(display_str(v), end="", file=f), (1, 2), any_p, op),
1597
+ "displayln": Proc("displayln",
1598
+ lambda v, f=None: print(display_str(v), file=f), (1, 2), any_p, op),
1599
+ "print": Proc("print",
1600
+ lambda v, f=None: print(print_str(v), end="", file=f), (1, 2), any_p, op),
1601
+ "println": Proc("println",
1602
+ lambda v, f=None: print(print_str(v), file=f), (1, 2), any_p, op),
1480
1603
  # actions
1481
- "assert": Proc("assert", palet_assert, (1, 2), [any_p, orc(is_str, False)]),
1482
- "display": Proc("display", lambda v: print(display_str(v), end=""), (1, 1)),
1483
- "displayln": Proc("displayln", lambda v: print(display_str(v)), (1, 1)),
1484
- "error": Proc("error", raise_, (1, 1), [is_str]),
1485
- "sleep": Proc("sleep", sleep, (1, 1), [is_int_or_float]),
1486
- "print": Proc("print", lambda v: print(print_str(v), end=""), (1, 1)),
1487
- "println": Proc("println", lambda v: print(print_str(v)), (1, 1)),
1488
- "system": Proc("system", palet_system, (1, 1)),
1604
+ "assert": Proc("assert", palet_assert, (1, 2), any_p, orc(is_str, False)),
1605
+ "error": Proc("error", raise_, (1, 1), is_str),
1606
+ "sleep": Proc("sleep", sleep, (1, 1), is_int_or_float),
1607
+ "system": Proc("system", palet_system, (1, 1), is_str),
1489
1608
  # conversions
1490
- "number->string": Proc("number->string", number_to_string, (1, 1), [is_num]),
1609
+ "number->string": Proc("number->string", number_to_string, (1, 1), is_num),
1491
1610
  "string->vector": Proc(
1492
- "string->vector", lambda s: [Char(c) for c in s], (1, 1), [is_str]
1611
+ "string->vector", lambda s: [Char(c) for c in s], (1, 1), is_str
1493
1612
  ),
1494
- "range->vector": Proc("range->vector", list, (1, 1), [is_range]),
1613
+ "range->vector": Proc("range->vector", list, (1, 1), is_range),
1495
1614
  # reflexion
1496
- "var-exists?": Proc("var-exists?", lambda sym: sym.val in env, (1, 1), [is_symbol]),
1615
+ "var-exists?": Proc("var-exists?", lambda sym: sym.val in env, (1, 1), is_symbol),
1497
1616
  "rename": Syntax(syn_rename),
1498
1617
  "delete": Syntax(syn_delete),
1499
1618
  })
@@ -1504,4 +1623,7 @@ def interpret(env: Env, parser: Parser) -> list:
1504
1623
  result = []
1505
1624
  while parser.current_token.type != EOF:
1506
1625
  result.append(my_eval(env, parser.expr()))
1626
+
1627
+ if type(result[-1]) is Keyword:
1628
+ raise MyError(f"Keyword misused in expression. `{result[-1]}`")
1507
1629
  return result