pyscript-programming-language 1.2.1__tar.gz → 1.2.2__tar.gz

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.

Potentially problematic release.


This version of pyscript-programming-language might be problematic. Click here for more details.

Files changed (51) hide show
  1. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/MANIFEST.in +1 -0
  2. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/PKG-INFO +2 -2
  3. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/README.md +1 -1
  4. pyscript_programming_language-1.2.2/changelog.md +12 -0
  5. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/interpreter.py +1 -1
  6. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/lexer.py +58 -45
  7. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/objects.py +6 -3
  8. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/parser.py +30 -36
  9. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/pysbuiltins.py +30 -25
  10. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/runner.py +23 -10
  11. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/singletons.py +12 -3
  12. pyscript_programming_language-1.2.2/pyscript/core/token.py +25 -0
  13. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/utils.py +22 -12
  14. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/validator.py +6 -5
  15. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/version.py +2 -2
  16. pyscript_programming_language-1.2.2/pyscript/lib/brainfuck.pys +191 -0
  17. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript_programming_language.egg-info/PKG-INFO +2 -2
  18. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript_programming_language.egg-info/SOURCES.txt +3 -5
  19. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/setup.py +1 -1
  20. pyscript_programming_language-1.2.1/pyscript/core/token.py +0 -21
  21. pyscript_programming_language-1.2.1/pyscript/lib/_tantee/__init__.pys +0 -50
  22. pyscript_programming_language-1.2.1/pyscript/lib/_tantee/animate.pys +0 -9
  23. pyscript_programming_language-1.2.1/pyscript/lib/_tantee/lyrics.pys +0 -12
  24. pyscript_programming_language-1.2.1/pyscript/lib/_tantee/tantee.mp3 +0 -0
  25. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/PyScript.png +0 -0
  26. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/__init__.py +0 -0
  27. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/__init__.pyi +0 -0
  28. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/__main__.py +0 -0
  29. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/__init__.py +0 -0
  30. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/bases.py +0 -0
  31. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/buffer.py +0 -0
  32. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/cache.py +0 -0
  33. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/constants.py +0 -0
  34. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/context.py +0 -0
  35. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/exceptions.py +0 -0
  36. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/handlers.py +0 -0
  37. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/highlight.py +0 -0
  38. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/nodes.py +0 -0
  39. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/position.py +0 -0
  40. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/results.py +0 -0
  41. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/core/symtab.py +0 -0
  42. /pyscript_programming_language-1.2.1/pyscript/lib/hello.pys → /pyscript_programming_language-1.2.2/pyscript/lib/__hello__.pys +0 -0
  43. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/lib/clock.pys +0 -0
  44. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/lib/getch.pys +0 -0
  45. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/lib/parser.pys +0 -0
  46. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/lib/sys.pys +0 -0
  47. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/lib/this.pys +0 -0
  48. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript/this.py +0 -0
  49. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript_programming_language.egg-info/dependency_links.txt +0 -0
  50. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/pyscript_programming_language.egg-info/top_level.txt +0 -0
  51. {pyscript_programming_language-1.2.1 → pyscript_programming_language-1.2.2}/setup.cfg +0 -0
@@ -1,3 +1,4 @@
1
+ include changelog.md
1
2
  include MANIFEST.in
2
3
  include PyScript.png
3
4
  include setup.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyscript-programming-language
3
- Version: 1.2.1
3
+ Version: 1.2.2
4
4
  Summary: PyScript Programming Language
5
5
  Home-page: https://github.com/azzammuhyala/pyscript
6
6
  Author: azzammuhyala
@@ -32,7 +32,7 @@ Dynamic: summary
32
32
  # PyScript Language📖
33
33
 
34
34
  <p align="center">
35
- <img src="PyScript.png" alt="PyScript Logo" width="200">
35
+ <img src="https://github.com/azzammuhyala/pyscript/blob/main/PyScript.png?raw=true" alt="PyScript Logo" width="200">
36
36
  </p>
37
37
 
38
38
  PyScript adalah bahasa pemprograman sederhana yang dibuat di atas bahasa Python. Bahasa ini mengabungkan beberapa sintaks dari Python dan JavaScript, jadi bagi Anda yang sudah familiar dengan sintaks Python atau JavaScript atau keduanya pasti seharusnya cukup mudah mempelajari bahasa ini.
@@ -1,7 +1,7 @@
1
1
  # PyScript Language📖
2
2
 
3
3
  <p align="center">
4
- <img src="PyScript.png" alt="PyScript Logo" width="200">
4
+ <img src="https://github.com/azzammuhyala/pyscript/blob/main/PyScript.png?raw=true" alt="PyScript Logo" width="200">
5
5
  </p>
6
6
 
7
7
  PyScript adalah bahasa pemprograman sederhana yang dibuat di atas bahasa Python. Bahasa ini mengabungkan beberapa sintaks dari Python dan JavaScript, jadi bagi Anda yang sudah familiar dengan sintaks Python atau JavaScript atau keduanya pasti seharusnya cukup mudah mempelajari bahasa ini.
@@ -0,0 +1,12 @@
1
+ # Change Log
2
+
3
+ ## [1.2.2] - 07-10-2025
4
+ ### Added
5
+ - _brainfuck_ default library.
6
+
7
+ ### Fixed
8
+ - Lexer or tokenization improvements on parsing unicode strings and numbers.
9
+ - Minor improvements to the parser.
10
+ - Newline system fixes in shell (_REPL_).
11
+ - Improvements `pyscript.core.version.version_info`.
12
+ - Improvements _builtins_ `ce`, `nce`, `increment`, and `decrement`.
@@ -804,7 +804,7 @@ class PysInterpreter(Pys):
804
804
  PysException(
805
805
  TypeError("exceptions must derive from BaseException"),
806
806
  context,
807
- node.position
807
+ node.target.position
808
808
  )
809
809
  )
810
810
 
@@ -13,13 +13,10 @@ class PysLexer(Pys):
13
13
 
14
14
  def __init__(self, file, context_parent=None, context_parent_entry_position=None, allowed_comment_token=False):
15
15
  self.file = file
16
- self.context = context_parent
16
+ self.context_parent = context_parent
17
17
  self.context_parent_entry_position = context_parent_entry_position
18
18
  self.allowed_comment_token = allowed_comment_token
19
19
 
20
- def peak(self, index):
21
- return self.file.text[index] if 0 <= index < len(self.file.text) else None
22
-
23
20
  def update_current_char(self):
24
21
  self.current_char = self.file.text[self.index] if 0 <= self.index < len(self.file.text) else None
25
22
 
@@ -53,29 +50,26 @@ class PysLexer(Pys):
53
50
  PysContext(
54
51
  name=None,
55
52
  file=self.file,
56
- parent=self.context,
53
+ parent=self.context_parent,
57
54
  parent_entry_position=self.context_parent_entry_position
58
55
  ),
59
56
  PysPosition(self.file, start, end or self.index)
60
57
  )
61
58
 
62
- def is_triple_quote(self, prefix):
63
- return all(self.peak(self.index + i) == prefix for i in range(3))
64
-
65
59
  def not_eof(self):
66
60
  return self.current_char is not None
67
61
 
68
62
  def char_eq(self, character):
69
- return self.current_char == character
63
+ return self.not_eof() and self.current_char == character
70
64
 
71
65
  def char_ne(self, character):
72
- return self.current_char != character
66
+ return self.not_eof() and self.current_char != character
73
67
 
74
68
  def char_in(self, characters):
75
69
  return self.not_eof() and self.current_char in characters
76
70
 
77
- def char_are(self, name_string_method, *args, **kwargs):
78
- return self.not_eof() and getattr(self.current_char, name_string_method)(*args, **kwargs)
71
+ def char_are(self, string_method, *args, **kwargs):
72
+ return self.not_eof() and getattr(self.current_char, string_method)(*args, **kwargs)
79
73
 
80
74
  def make_tokens(self):
81
75
  self.index = -1
@@ -214,8 +208,8 @@ class PysLexer(Pys):
214
208
 
215
209
  def make_number(self):
216
210
  start = self.index
217
- number = ''
218
211
  format = int
212
+ number = ''
219
213
 
220
214
  is_scientific = False
221
215
  is_complex = False
@@ -224,8 +218,19 @@ class PysLexer(Pys):
224
218
  if self.char_eq('.'):
225
219
  format = float
226
220
  number = '.'
221
+
227
222
  self.advance()
228
223
 
224
+ if self.file.text[self.index:self.index + 2] == '..':
225
+ self.advance()
226
+ self.advance()
227
+ self.add_token(TOKENS['ELLIPSIS'], start)
228
+ return
229
+
230
+ elif not self.char_in('0123456789'):
231
+ self.add_token(TOKENS['DOT'], start)
232
+ return
233
+
229
234
  while self.char_in('0123456789'):
230
235
  number += self.current_char
231
236
  self.advance()
@@ -238,6 +243,7 @@ class PysLexer(Pys):
238
243
 
239
244
  elif self.char_eq('.') and not is_scientific and format is int:
240
245
  format = float
246
+
241
247
  number += '.'
242
248
  self.advance()
243
249
 
@@ -248,6 +254,7 @@ class PysLexer(Pys):
248
254
 
249
255
  format = str
250
256
  number = ''
257
+
251
258
  character_base = self.char_are('lower')
252
259
 
253
260
  if character_base == 'b':
@@ -303,12 +310,7 @@ class PysLexer(Pys):
303
310
  if self.char_eq('.'):
304
311
  self.advance()
305
312
 
306
- if format is float and self.peak(self.index) == '.':
307
- self.advance()
308
- self.add_token(TOKENS['ELLIPSIS'], start)
309
- return
310
-
311
- if number != '.' and (format is float or is_complex or is_scientific):
313
+ if format is float or is_complex or is_scientific:
312
314
  self.throw(self.index - 1, "invalid decimal literal")
313
315
 
314
316
  format = float
@@ -320,10 +322,7 @@ class PysLexer(Pys):
320
322
  return complex(0, result) if is_complex else result
321
323
 
322
324
  if format is float:
323
- if number == '.':
324
- self.add_token(TOKENS['DOT'], start)
325
- else:
326
- self.add_token(TOKENS['NUMBER'], start, wrap(float))
325
+ self.add_token(TOKENS['NUMBER'], start, wrap(float))
327
326
 
328
327
  elif format is str:
329
328
  self.add_token(TOKENS['NUMBER'], start, wrap(int, base))
@@ -353,17 +352,25 @@ class PysLexer(Pys):
353
352
 
354
353
  prefix = self.current_char
355
354
 
356
- is_triple_quote = self.is_triple_quote(prefix)
355
+ def triple_quote():
356
+ return self.file.text[self.index:self.index + 3] == prefix * 3
357
+
358
+ is_triple_quote = triple_quote()
357
359
  warning_displayed = False
358
360
  decoded_error_message = None
359
361
 
362
+ def decode_error(message):
363
+ nonlocal decoded_error_message
364
+ if decoded_error_message is None:
365
+ decoded_error_message = message
366
+
360
367
  self.advance()
361
368
 
362
369
  if is_triple_quote:
363
370
  self.advance()
364
371
  self.advance()
365
372
 
366
- while self.not_eof() and not (self.is_triple_quote(prefix) if is_triple_quote else self.char_in(prefix + '\n')):
373
+ while self.not_eof() and not (triple_quote() if is_triple_quote else self.char_in(prefix + '\n')):
367
374
 
368
375
  if self.char_eq('\\'):
369
376
  self.advance()
@@ -391,62 +398,68 @@ class PysLexer(Pys):
391
398
  string += '\a'
392
399
  elif self.char_eq('v'):
393
400
  string += '\v'
401
+
394
402
  self.advance()
395
403
 
396
404
  elif decoded_error_message is None:
397
405
  escape = ''
398
406
 
399
407
  if self.char_in('01234567'):
400
- while self.not_eof() and self.char_in('01234567') and len(escape) < 3:
408
+
409
+ while self.char_in('01234567') and len(escape) < 3:
401
410
  escape += self.current_char
402
411
  self.advance()
403
412
 
404
413
  string += chr(int(escape, 8))
405
414
 
406
415
  elif self.char_in('xuU'):
407
- if self.current_char == 'x':
416
+ base = self.current_char
417
+
418
+ if base == 'x':
408
419
  length = 2
409
- elif self.current_char == 'u':
420
+ elif base == 'u':
410
421
  length = 4
411
- elif self.current_char == 'U':
422
+ elif base == 'U':
412
423
  length = 8
413
424
 
414
- base = self.current_char
415
-
416
425
  self.advance()
417
426
 
418
- while self.not_eof() and self.char_in('0123456789ABCDEFabcdef') and len(escape) < length:
427
+ while self.char_in('0123456789ABCDEFabcdef') and len(escape) < length:
419
428
  escape += self.current_char
420
429
  self.advance()
421
430
 
422
431
  if len(escape) != length:
423
- decoded_error_message = "codec can't decode bytes, truncated \\{}{} escape".format(
424
- base, 'X' * length
425
- )
432
+ decode_error("codec can't decode bytes, truncated \\{}{} escape".format(base, 'X' * length))
433
+
426
434
  else:
427
- string += chr(int(escape, 16))
435
+ try:
436
+ string += chr(int(escape, 16))
437
+ except:
438
+ decode_error("codec can't decode bytes: illegal Unicode character")
428
439
 
429
440
  elif self.char_eq('N'):
430
441
  self.advance()
431
442
 
432
443
  if self.current_char != '{':
433
- decoded_error_message = "malformed \\N character escape"
444
+ decode_error("malformed \\N character escape")
434
445
  continue
435
446
 
436
447
  self.advance()
437
448
 
438
- while self.not_eof() and self.char_ne('}'):
449
+ while self.char_ne('}'):
439
450
  escape += self.current_char
440
451
  self.advance()
441
452
 
442
453
  if self.current_char == '}':
443
454
  try:
444
455
  string += unicode_lookup(escape)
445
- except KeyError:
446
- decoded_error_message = "codec can't decode bytes, unknown Unicode character name"
456
+ except:
457
+ decode_error("codec can't decode bytes, unknown Unicode character name")
458
+
447
459
  self.advance()
460
+
448
461
  else:
449
- decoded_error_message = "malformed \\N character escape"
462
+ decode_error("malformed \\N character escape")
450
463
 
451
464
  else:
452
465
  if not self.not_eof():
@@ -467,10 +480,10 @@ class PysLexer(Pys):
467
480
  string += self.current_char
468
481
  self.advance()
469
482
 
470
- if not (self.is_triple_quote(prefix) if is_triple_quote else self.char_eq(prefix)):
483
+ if not (triple_quote() if is_triple_quote else self.char_eq(prefix)):
471
484
  self.throw(start, "unterminated bytes literal" if is_bytes else "unterminated string literal", start + 1)
472
485
 
473
- elif decoded_error_message:
486
+ elif decoded_error_message is not None:
474
487
  self.advance()
475
488
  self.throw(start, decoded_error_message)
476
489
 
@@ -507,7 +520,7 @@ class PysLexer(Pys):
507
520
 
508
521
  self.advance()
509
522
 
510
- while self.not_eof() and self.char_ne('\n') and self.char_are('isspace'):
523
+ while self.char_ne('\n') and self.char_are('isspace'):
511
524
  self.advance()
512
525
 
513
526
  if not self.char_are('isidentifier'):
@@ -744,7 +757,7 @@ class PysLexer(Pys):
744
757
 
745
758
  self.advance()
746
759
 
747
- while self.not_eof() and self.char_ne('\n'):
760
+ while self.char_ne('\n'):
748
761
  comment += self.current_char
749
762
  self.advance()
750
763
 
@@ -1,7 +1,10 @@
1
1
  from .bases import Pys
2
2
  from .buffer import PysCode
3
3
 
4
- class PysModule(Pys):
4
+ class PysObject(Pys):
5
+ pass
6
+
7
+ class PysModule(PysObject):
5
8
 
6
9
  def __init__(self, name, doc=None):
7
10
  self.__name__ = name
@@ -17,7 +20,7 @@ class PysModule(Pys):
17
20
  '' if file is undefined else ' from {!r}'.format(file)
18
21
  )
19
22
 
20
- class PysChainFunction(Pys):
23
+ class PysChainFunction(PysObject):
21
24
 
22
25
  def __init__(self, func):
23
26
  from .constants import DEFAULT
@@ -35,7 +38,7 @@ class PysChainFunction(Pys):
35
38
  handle_call(self.__func__, self.__code__.context, self.__code__.position, self.__code__.flags)
36
39
  return self.__func__(self, *args, **kwargs)
37
40
 
38
- class PysFunction(Pys):
41
+ class PysFunction(PysObject):
39
42
 
40
43
  def __init__(self, name, parameters, body, position, context):
41
44
  from .constants import DEFAULT
@@ -22,6 +22,10 @@ class PysParser(Pys):
22
22
 
23
23
  self.advance()
24
24
 
25
+ def update_current_token(self):
26
+ if 0 <= self.token_index < len(self.tokens):
27
+ self.current_token = self.tokens[self.token_index]
28
+
25
29
  def advance(self):
26
30
  self.token_index += 1
27
31
  self.update_current_token()
@@ -30,10 +34,6 @@ class PysParser(Pys):
30
34
  self.token_index -= amount
31
35
  self.update_current_token()
32
36
 
33
- def update_current_token(self):
34
- if 0 <= self.token_index < len(self.tokens):
35
- self.current_token = self.tokens[self.token_index]
36
-
37
37
  def error(self, message, position=None):
38
38
  return PysException(
39
39
  SyntaxError(message),
@@ -89,11 +89,7 @@ class PysParser(Pys):
89
89
  PysSequenceNode(
90
90
  'statements',
91
91
  statements,
92
- PysPosition(
93
- self.file,
94
- start,
95
- self.current_token.position.end
96
- )
92
+ PysPosition(self.file, start, self.current_token.position.end)
97
93
  )
98
94
  )
99
95
 
@@ -435,7 +431,7 @@ class PysParser(Pys):
435
431
  self.advance()
436
432
  self.skip(result)
437
433
 
438
- on_keyword_state = False
434
+ seen_kwargs = False
439
435
  args = []
440
436
 
441
437
  while self.current_token.type not in parenthesises_map.values():
@@ -454,12 +450,12 @@ class PysParser(Pys):
454
450
  self.advance()
455
451
  self.skip(result)
456
452
 
457
- on_keyword_state = True
453
+ seen_kwargs = True
458
454
 
459
- elif on_keyword_state:
455
+ elif seen_kwargs:
460
456
  return result.failure(self.error("expected '=' (follows keyword argument)"))
461
457
 
462
- if on_keyword_state:
458
+ if seen_kwargs:
463
459
  value = result.register(self.expr(), True)
464
460
  if result.error:
465
461
  return result
@@ -697,11 +693,10 @@ class PysParser(Pys):
697
693
 
698
694
  return result.failure(self.error("expected expression", token.position), fatal=False)
699
695
 
700
- def sequence_expr(self, type):
696
+ def sequence_expr(self, type, should_sequence=False):
701
697
  result = PysParserResult()
702
698
  start = self.current_token.position.start
703
699
 
704
- should_sequence = True
705
700
  elements = []
706
701
 
707
702
  left_parenthesis = parenthesises_sequence_map[type]
@@ -755,9 +750,9 @@ class PysParser(Pys):
755
750
  return result.failure(self.error("invalid syntax. Perhaps you forgot a comma?"))
756
751
 
757
752
  else:
758
- should_sequence = False
759
753
 
760
754
  while self.current_token.type not in parenthesises_map.values():
755
+
761
756
  elements.append(result.register(self.expr(), True))
762
757
  if result.error:
763
758
  return result
@@ -1009,12 +1004,11 @@ class PysParser(Pys):
1009
1004
 
1010
1005
  return result.success(all_cases)
1011
1006
 
1012
- else:
1013
- else_body = result.register(self.else_expr())
1014
- if result.error:
1015
- return result
1007
+ else_body = result.register(self.else_expr())
1008
+ if result.error:
1009
+ return result
1016
1010
 
1017
- return result.success(([], else_body))
1011
+ return result.success(([], else_body))
1018
1012
 
1019
1013
  def if_expr_cases(self, case_keyword):
1020
1014
  result = PysParserResult()
@@ -1148,8 +1142,8 @@ class PysParser(Pys):
1148
1142
  return result
1149
1143
 
1150
1144
  return result.success(body)
1151
- else:
1152
- return result.success(None)
1145
+
1146
+ return result.success(None)
1153
1147
 
1154
1148
  def case_or_default_expr(self):
1155
1149
  result = PysParserResult()
@@ -1161,12 +1155,11 @@ class PysParser(Pys):
1161
1155
 
1162
1156
  return result.success(all_cases)
1163
1157
 
1164
- else:
1165
- default_body = result.register(self.default_expr())
1166
- if result.error:
1167
- return result
1158
+ default_body = result.register(self.default_expr())
1159
+ if result.error:
1160
+ return result
1168
1161
 
1169
- return result.success(([], default_body))
1162
+ return result.success(([], default_body))
1170
1163
 
1171
1164
  def try_expr(self):
1172
1165
  result = PysParserResult()
@@ -1316,7 +1309,7 @@ class PysParser(Pys):
1316
1309
  self.advance()
1317
1310
  self.skip(result)
1318
1311
 
1319
- iterable = result.register(self.expr(), True)
1312
+ iter = result.register(self.expr(), True)
1320
1313
  if result.error:
1321
1314
  return result
1322
1315
 
@@ -1354,7 +1347,7 @@ class PysParser(Pys):
1354
1347
 
1355
1348
  return result.success(
1356
1349
  PysForNode(
1357
- (init, iterable) if foreach else (init, condition, update),
1350
+ (init, iter) if foreach else (init, condition, update),
1358
1351
  body,
1359
1352
  else_body,
1360
1353
  position
@@ -1466,11 +1459,12 @@ class PysParser(Pys):
1466
1459
  self.skip(result)
1467
1460
 
1468
1461
  if self.current_token.type == TOKENS['LPAREN']:
1469
- bases = result.register(self.sequence_expr('tuple'))
1462
+ bases = result.register(self.sequence_expr('tuple', should_sequence=True))
1470
1463
  if result.error:
1471
1464
  return result
1472
1465
 
1473
- bases = bases.elements if isinstance(bases, PysSequenceNode) else [bases]
1466
+ end = bases.position.end
1467
+ bases = bases.elements
1474
1468
 
1475
1469
  else:
1476
1470
  bases = []
@@ -1525,7 +1519,7 @@ class PysParser(Pys):
1525
1519
  self.advance()
1526
1520
  self.skip(result)
1527
1521
 
1528
- on_keyword_state = False
1522
+ seen_kwargs = False
1529
1523
  parameters = []
1530
1524
 
1531
1525
  while self.current_token.type not in parenthesises_map.values():
@@ -1544,12 +1538,12 @@ class PysParser(Pys):
1544
1538
  self.advance()
1545
1539
  self.skip(result)
1546
1540
 
1547
- on_keyword_state = True
1541
+ seen_kwargs = True
1548
1542
 
1549
- elif on_keyword_state:
1543
+ elif seen_kwargs:
1550
1544
  return result.failure(self.error("expected '=' (follows keyword argument)"))
1551
1545
 
1552
- if on_keyword_state:
1546
+ if seen_kwargs:
1553
1547
  value = result.register(self.expr(), True)
1554
1548
  if result.error:
1555
1549
  return result
@@ -193,14 +193,13 @@ def ce(a, b, *, rel_tol=1e-9, abs_tol=0):
193
193
  from math import isclose
194
194
  return isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol)
195
195
 
196
- try:
197
- from_a = True
198
- func = getattr(a, '__ce__')
199
- except AttributeError:
200
- from_a = False
201
- func = getattr(b, '__ce__', None)
196
+ from .utils import supported_method
197
+
198
+ success, result = supported_method(a, '__ce__', b, rel_tol=rel_tol, abs_tol=abs_tol)
199
+ if not success:
200
+ success, result = supported_method(b, '__ce__', a, rel_tol=rel_tol, abs_tol=abs_tol)
202
201
 
203
- if not callable(func):
202
+ if not success:
204
203
  raise TypeError(
205
204
  "unsupported operand type(s) for ~= or ce(): {!r} and {!r}".format(
206
205
  type(a).__name__,
@@ -208,24 +207,26 @@ def ce(a, b, *, rel_tol=1e-9, abs_tol=0):
208
207
  )
209
208
  )
210
209
 
211
- return func(b if from_a else a, rel_tol=rel_tol, abs_tol=abs_tol)
210
+ return result
212
211
 
213
212
  def nce(a, b, *, rel_tol=1e-9, abs_tol=0):
214
213
  if isinstance(a, (int, float)) and isinstance(b, (int, float)):
215
214
  from math import isclose
216
215
  return not isclose(a, b, rel_tol=rel_tol, abs_tol=abs_tol)
217
216
 
218
- try:
219
- from_a = True
220
- func = getattr(a, '__nce__')
221
- except AttributeError:
222
- try:
223
- from_a = False
224
- func = getattr(b, '__nce__')
225
- except AttributeError:
226
- return not ce(a, b, rel_tol=rel_tol, abs_tol=abs_tol)
217
+ from .utils import supported_method
227
218
 
228
- if not callable(func):
219
+ success, result = supported_method(a, '__nce__', b, rel_tol=rel_tol, abs_tol=abs_tol)
220
+ if not success:
221
+ success, result = supported_method(b, '__nce__', a, rel_tol=rel_tol, abs_tol=abs_tol)
222
+ if not success:
223
+ success, result = supported_method(a, '__ce__', b, rel_tol=rel_tol, abs_tol=abs_tol)
224
+ if not success:
225
+ success, result = supported_method(b, '__ce__', a, rel_tol=rel_tol, abs_tol=abs_tol)
226
+
227
+ result = not result
228
+
229
+ if not success:
229
230
  raise TypeError(
230
231
  "unsupported operand type(s) for ~! or nce(): {!r} and {!r}".format(
231
232
  type(a).__name__,
@@ -233,29 +234,33 @@ def nce(a, b, *, rel_tol=1e-9, abs_tol=0):
233
234
  )
234
235
  )
235
236
 
236
- return func(b if from_a else a, rel_tol=rel_tol, abs_tol=abs_tol)
237
+ return result
237
238
 
238
239
  def increment(object):
239
240
  if isinstance(object, (int, float)):
240
241
  return object + 1
241
242
 
242
- func = getattr(object, '__increment__', None)
243
+ from .utils import supported_method
243
244
 
244
- if not callable(func):
245
+ success, result = supported_method(object, '__increment__')
246
+
247
+ if not success:
245
248
  raise TypeError("unsupported operand type(s) for ++ or increment(): {!r}".format(type(object).__name__))
246
249
 
247
- return func()
250
+ return result
248
251
 
249
252
  def decrement(object):
250
253
  if isinstance(object, (int, float)):
251
254
  return object - 1
252
255
 
253
- func = getattr(object, '__decrement__', None)
256
+ from .utils import supported_method
257
+
258
+ success, result = supported_method(object, '__decrement__')
254
259
 
255
- if not callable(func):
260
+ if not success:
256
261
  raise TypeError("unsupported operand type(s) for -- or decrement(): {!r}".format(type(object).__name__))
257
262
 
258
- return func()
263
+ return result
259
264
 
260
265
  def comprehension(init, wrap, condition=None):
261
266
  if not callable(wrap):