rapydscript-ns 0.8.1 → 0.8.2

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.
@@ -47,16 +47,49 @@ def print_do_loop(self, output):
47
47
  output.space()
48
48
  output.print("while")
49
49
  output.space()
50
- output.with_parens(def(): self.condition.print(output);)
50
+ if self.python_truthiness:
51
+ output.with_parens(def(): output.print('ρσ_bool('), self.condition.print(output), output.print(')');)
52
+ else:
53
+ output.with_parens(def(): self.condition.print(output);)
51
54
  output.semicolon()
52
55
 
53
56
  def print_while_loop(self, output):
57
+ # while/else: wrap in a labeled block; break inside will skip the else
58
+ forelse_label = None
59
+ if self.belse:
60
+ forelse_label = output.new_forelse_label()
61
+ output.forelse_stack.push(forelse_label)
62
+ output.print(forelse_label + ':')
63
+ output.space()
64
+ output.print('{')
65
+ output.newline()
66
+ output.set_indentation(output.next_indent())
67
+ output.indent()
68
+ else:
69
+ output.forelse_stack.push(None)
70
+
54
71
  output.print("while")
55
72
  output.space()
56
- output.with_parens(def(): self.condition.print(output);)
73
+ if self.python_truthiness:
74
+ output.with_parens(def(): output.print('ρσ_bool('), self.condition.print(output), output.print(')');)
75
+ else:
76
+ output.with_parens(def(): self.condition.print(output);)
57
77
  output.space()
58
78
  self._do_print_body(output)
59
79
 
80
+ output.forelse_stack.pop()
81
+
82
+ if forelse_label:
83
+ if self.belse and self.belse.body and self.belse.body.length:
84
+ output.newline()
85
+ for stmt in self.belse.body:
86
+ output.indent()
87
+ stmt.print(output)
88
+ output.newline()
89
+ output.set_indentation(output._indentation - output.options.indent_level)
90
+ output.indent()
91
+ output.print('}')
92
+
60
93
  def is_simple_for_in(self):
61
94
  # return true if this loop can be simplified into a basic for (i in j) loop
62
95
  if is_node_type(self.object, AST_BaseCall)
@@ -131,6 +164,21 @@ def print_for_in(self, output):
131
164
  else:
132
165
  self.object.print(output)
133
166
 
167
+ # for/else: wrap in a labeled block; break inside will use the label to skip else
168
+ forelse_label = None
169
+ if self.belse:
170
+ forelse_label = output.new_forelse_label()
171
+ output.forelse_stack.push(forelse_label)
172
+ output.print(forelse_label + ':')
173
+ output.space()
174
+ output.print('{')
175
+ output.newline()
176
+ output.set_indentation(output.next_indent())
177
+ output.indent()
178
+ else:
179
+ # Push null so nested for loops don't mistakenly grab an outer forelse label
180
+ output.forelse_stack.push(None)
181
+
134
182
  if is_simple_for(self):
135
183
  # optimize range() into a simple for loop
136
184
  increment = None
@@ -227,6 +275,20 @@ def print_for_in(self, output):
227
275
  output.space()
228
276
  self._do_print_body(output)
229
277
 
278
+ output.forelse_stack.pop()
279
+
280
+ if forelse_label:
281
+ # Emit else body, then close the labeled block
282
+ if self.belse and self.belse.body and self.belse.body.length:
283
+ output.newline()
284
+ for stmt in self.belse.body:
285
+ output.indent()
286
+ stmt.print(output)
287
+ output.newline()
288
+ output.set_indentation(output._indentation - output.options.indent_level)
289
+ output.indent()
290
+ output.print('}')
291
+
230
292
  def print_list_comprehension(self, output):
231
293
  tname = self.constructor.name.slice(4)
232
294
  result_obj = {'ListComprehension':'[]', 'DictComprehension':('Object.create(null)' if self.is_jshash else '{}'), 'SetComprehension':'ρσ_set()'}[tname]
@@ -110,6 +110,16 @@ def print_unary_prefix(self, output):
110
110
  if op is '~':
111
111
  output.print('ρσ_op_invert('), self.expression.print(output), output.print(')')
112
112
  return
113
+ if op is '!' and self.python_truthiness:
114
+ output.print('!ρσ_bool(')
115
+ if self.parenthesized:
116
+ output.with_parens(def():
117
+ self.expression.print(output)
118
+ )
119
+ else:
120
+ self.expression.print(output)
121
+ output.print(')')
122
+ return
113
123
  output.print(op)
114
124
  if /^[a-z]/i.test(op):
115
125
  output.space()
@@ -284,6 +294,36 @@ def print_binary_op(self, output):
284
294
  output.spaced(nan_check, '!==' if self.operator is '===' else '===', nan_check)
285
295
  else:
286
296
  output.spaced(self.left, self.operator, self.right)
297
+ elif (self.operator is '&&' or self.operator is '||') and self.python_truthiness:
298
+ # Python `and`/`or` use Python truthiness (only when from __python__ import truthiness is set).
299
+ # `a and b` → `ρσ_bool(a) ? b : a` (returns a if falsy, else b)
300
+ # `a or b` → `ρσ_bool(a) ? a : b` (returns a if truthy, else b)
301
+ # For non-simple left operands, cache in ρσ_expr_temp to avoid double evaluation.
302
+ is_and = self.operator is '&&'
303
+ if is_node_type(self.left, AST_SymbolRef):
304
+ output.print('(ρσ_bool(')
305
+ self.left.print(output)
306
+ output.print(') ? ')
307
+ if is_and:
308
+ self.right.print(output)
309
+ output.print(' : ')
310
+ self.left.print(output)
311
+ else:
312
+ self.left.print(output)
313
+ output.print(' : ')
314
+ self.right.print(output)
315
+ output.print(')')
316
+ else:
317
+ output.print('(ρσ_expr_temp = ')
318
+ self.left.print(output)
319
+ output.print(', ρσ_bool(ρσ_expr_temp) ? ')
320
+ if is_and:
321
+ self.right.print(output)
322
+ output.print(' : ρσ_expr_temp)')
323
+ else:
324
+ output.print('ρσ_expr_temp : ')
325
+ self.right.print(output)
326
+ output.print(')')
287
327
  else:
288
328
  output.spaced(self.left, self.operator, self.right)
289
329
 
@@ -368,6 +408,13 @@ def print_assign(self, output):
368
408
  self.right.print(output)
369
409
  )
370
410
  return
411
+ if self.operator is '**=':
412
+ output.assign(self.left)
413
+ if output.options.js_version > 6:
414
+ output.print('(('), self.left.print(output), output.spaced(')', '**', '('), self.right.print(output), output.print('))')
415
+ else:
416
+ output.print('Math.pow('), self.left.print(output), output.comma(), self.right.print(output), output.print(')')
417
+ return
371
418
  if self.operator is '=' and self.is_chained():
372
419
  left_hand_sides, rhs = self.traverse_chain()
373
420
  is_compound_assign = False
@@ -390,7 +437,12 @@ def print_assign(self, output):
390
437
 
391
438
  def print_conditional(self, output, condition, consequent, alternative):
392
439
  condition, consequent, alternative = self.condition, self.consequent, self.alternative
393
- output.with_parens(def():condition.print(output);)
440
+ if self.python_truthiness:
441
+ output.print('ρσ_bool(')
442
+ condition.print(output)
443
+ output.print(')')
444
+ else:
445
+ output.with_parens(def(): condition.print(output);)
394
446
  output.space()
395
447
  output.print("?")
396
448
  output.space()
@@ -156,15 +156,16 @@ def print_with(self, output):
156
156
  output.with_block(def():
157
157
  output.indent(), output.assign('ρσ_with_exception'), output.print('e'), output.end_statement()
158
158
  )
159
+ reversed_exits = exits.slice().reverse()
159
160
  output.newline(), output.indent(), output.spaced('if', '(ρσ_with_exception', '===', 'undefined)')
160
161
  output.with_block(def():
161
- for clause in exits:
162
+ for clause in reversed_exits:
162
163
  output.indent(), output.print(clause + '.__exit__()'), output.end_statement()
163
164
  )
164
165
  output.space(), output.print('else'), output.space()
165
166
  output.with_block(def():
166
167
  output.indent(), output.assign('ρσ_with_suppress'), output.print('false'), output.end_statement()
167
- for clause in exits:
168
+ for clause in reversed_exits:
168
169
  output.indent()
169
170
  output.spaced('ρσ_with_suppress', '|=', 'ρσ_bool(' + clause + '.__exit__(ρσ_with_exception.constructor,',
170
171
  'ρσ_with_exception,', 'ρσ_with_exception.stack))')
@@ -175,7 +176,10 @@ def print_with(self, output):
175
176
  def print_assert(self, output):
176
177
  if output.options.discard_asserts:
177
178
  return
178
- output.spaced('if', '(!('), self.condition.print(output), output.spaced('))', 'throw new AssertionError')
179
+ if self.python_truthiness:
180
+ output.spaced('if', '(!ρσ_bool('), self.condition.print(output), output.spaced('))', 'throw new AssertionError')
181
+ else:
182
+ output.spaced('if', '(!('), self.condition.print(output), output.spaced('))', 'throw new AssertionError')
179
183
  if self.message:
180
184
  output.print('(')
181
185
  self.message.print(output)
@@ -76,6 +76,12 @@ class OutputStream:
76
76
  self.with_counter = 0
77
77
  self.try_else_counter = 0
78
78
  self.match_counter = 0
79
+ self.forelse_counter = 0
80
+ self.forelse_stack = v'[]'
81
+
82
+ def new_forelse_label(self):
83
+ self.forelse_counter += 1
84
+ return 'ρσ_forelse_' + self.forelse_counter
79
85
 
80
86
  def new_try_else_counter(self):
81
87
  self.try_else_counter += 1
package/src/parse.pyj CHANGED
@@ -24,12 +24,13 @@ AST_Undefined, AST_Var, AST_VarDef, AST_Verbatim, AST_While, AST_With, AST_WithC
24
24
  AST_Yield, AST_Await, AST_Assert, AST_Existential, AST_NamedExpr, AST_AnnotatedAssign, AST_Super, AST_Starred, is_node_type,
25
25
  AST_Match, AST_MatchCase,
26
26
  AST_MatchWildcard, AST_MatchCapture, AST_MatchLiteral, AST_MatchOr,
27
- AST_MatchAs, AST_MatchStar, AST_MatchSequence, AST_MatchMapping, AST_MatchClass
27
+ AST_MatchAs, AST_MatchStar, AST_MatchSequence, AST_MatchMapping, AST_MatchClass,
28
+ TreeWalker
28
29
  )
29
30
  from tokenizer import tokenizer, is_token, RESERVED_WORDS
30
31
 
31
32
  COMPILER_VERSION = '__COMPILER_VERSION__'
32
- PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True}
33
+ PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True, 'overload_operators':True, 'truthiness':True}
33
34
 
34
35
 
35
36
  def get_compiler_version():
@@ -116,7 +117,7 @@ FORBIDDEN_CLASS_VARS = 'prototype constructor'.split(' ')
116
117
  # -----[ Parser (constants) ]-----
117
118
  UNARY_PREFIX = make_predicate('typeof void delete ~ - + ! @')
118
119
 
119
- ASSIGNMENT = make_predicate('= += -= /= //= *= %= >>= <<= >>>= |= ^= &=')
120
+ ASSIGNMENT = make_predicate('= += -= /= //= *= **= %= >>= <<= >>>= |= ^= &=')
120
121
 
121
122
  PRECEDENCE = (def(a, ret):
122
123
  for i in range(a.length):
@@ -336,7 +337,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
336
337
  continue
337
338
 
338
339
  # recursive descent into conditional, loop and exception bodies
339
- for option in ('body', 'alternative', 'bcatch', 'condition'):
340
+ for option in ('body', 'alternative', 'bcatch', 'belse', 'condition'):
340
341
  opt = stmt[option]
341
342
  if opt:
342
343
  extend(scan_for_local_vars(opt))
@@ -402,6 +403,23 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
402
403
 
403
404
  elif is_node_type(body, AST_ForIn):
404
405
  add_for_in(body)
406
+ # Also scan conditions for walrus operators (e.g. comprehension filters)
407
+ if body.condition:
408
+ extend(scan_for_local_vars(body.condition))
409
+ if body.clauses:
410
+ for clause in body.clauses:
411
+ if clause.condition:
412
+ extend(scan_for_local_vars(clause.condition))
413
+
414
+ else:
415
+ # Generic expression node — walk for walrus operators, stopping at scope boundaries
416
+ if body.walk:
417
+ body.walk(new TreeWalker(def(node):
418
+ if is_node_type(node, AST_NamedExpr):
419
+ push(node.name.name)
420
+ if is_node_type(node, AST_Scope):
421
+ return True # prune: don't descend into inner function scopes
422
+ ))
405
423
 
406
424
  return localvars
407
425
 
@@ -550,7 +568,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
550
568
  croak('Assignments in do loop conditions are not allowed')
551
569
  semicolon()
552
570
  return tmp
553
- )()
571
+ )(),
572
+ 'python_truthiness': S.scoped_flags.get('truthiness', False)
554
573
  })
555
574
  elif tmp_ is "while":
556
575
  while_cond = expression(True)
@@ -558,9 +577,21 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
558
577
  croak('Assignments in while loop conditions are not allowed')
559
578
  if not is_('punc', ':'):
560
579
  croak('Expected a colon after the while statement')
580
+ while_body = in_loop(statement)
581
+ while_belse = None
582
+ if is_("keyword", "else"):
583
+ start = S.token
584
+ next()
585
+ while_belse = new AST_Else({
586
+ 'start': start,
587
+ 'body': block_(),
588
+ 'end': prev()
589
+ })
561
590
  return new AST_While({
562
591
  'condition': while_cond,
563
- 'body': in_loop(statement)
592
+ 'body': while_body,
593
+ 'belse': while_belse,
594
+ 'python_truthiness': S.scoped_flags.get('truthiness', False)
564
595
  })
565
596
  elif tmp_ is "for":
566
597
  if is_('js'):
@@ -622,7 +653,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
622
653
  if is_('punc', ','):
623
654
  next()
624
655
  msg = expression(False)
625
- return new AST_Assert({'start': start, 'condition':cond, 'message':msg, 'end':prev()})
656
+ return new AST_Assert({'start': start, 'condition':cond, 'message':msg, 'end':prev(), 'python_truthiness': S.scoped_flags.get('truthiness', False)})
626
657
  elif tmp_ is "if":
627
658
  return if_()
628
659
  elif tmp_ is "pass":
@@ -647,10 +678,17 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
647
678
  })
648
679
 
649
680
  tmp = expression(True)
681
+ cause = None
682
+ if is_("keyword", "from"):
683
+ next()
684
+ cause = expression(True)
650
685
  semicolon()
651
- return new AST_Throw({
686
+ node = new AST_Throw({
652
687
  'value': tmp
653
688
  })
689
+ if cause is not None:
690
+ node.cause = cause
691
+ return node
654
692
  elif tmp_ is "try":
655
693
  return try_()
656
694
  elif tmp_ is "nonlocal":
@@ -1116,11 +1154,22 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1116
1154
  'object': obj
1117
1155
  }
1118
1156
 
1157
+ body = in_loop(statement)
1158
+ belse = None
1159
+ if is_("keyword", "else"):
1160
+ start = S.token
1161
+ next()
1162
+ belse = new AST_Else({
1163
+ 'start': start,
1164
+ 'body': block_(),
1165
+ 'end': prev()
1166
+ })
1119
1167
  return new AST_ForIn({
1120
1168
  'init': init,
1121
1169
  'name': lhs,
1122
1170
  'object': obj,
1123
- 'body': in_loop(statement)
1171
+ 'body': body,
1172
+ 'belse': belse
1124
1173
  })
1125
1174
 
1126
1175
  # A native JavaScript for loop - for v"var i=0; i<5000; i++":
@@ -1865,7 +1914,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1865
1914
  return new AST_If({
1866
1915
  'condition': cond,
1867
1916
  'body': body,
1868
- 'alternative': belse
1917
+ 'alternative': belse,
1918
+ 'python_truthiness': S.scoped_flags.get('truthiness', False)
1869
1919
  })
1870
1920
 
1871
1921
  def is_docstring(stmt):
@@ -1917,10 +1967,19 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1917
1967
  next()
1918
1968
  exceptions = []
1919
1969
  if not is_("punc", ":") and not is_("keyword", "as"):
1970
+ # Accept both: except TypeError, ValueError:
1971
+ # and: except (TypeError, ValueError):
1972
+ paren_wrapped = is_("punc", "(")
1973
+ if paren_wrapped:
1974
+ next()
1920
1975
  exceptions.push(as_symbol(AST_SymbolVar))
1921
1976
  while is_("punc", ","):
1922
1977
  next()
1978
+ if paren_wrapped and is_("punc", ")"):
1979
+ break # trailing comma inside parens
1923
1980
  exceptions.push(as_symbol(AST_SymbolVar))
1981
+ if paren_wrapped:
1982
+ expect(")")
1924
1983
 
1925
1984
  name = None
1926
1985
  if is_("keyword", "as"):
@@ -2670,7 +2729,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2670
2729
  'start': start,
2671
2730
  'expression': expr,
2672
2731
  'args': func_call_list(),
2673
- 'end': prev()
2732
+ 'end': prev(),
2733
+ 'python_truthiness': S.scoped_flags.get('truthiness', False) and is_node_type(expr, AST_SymbolRef)
2674
2734
  }), True)
2675
2735
  S.in_parenthesized_expr = False
2676
2736
  return ret
@@ -2760,6 +2820,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2760
2820
  ex.end = prev()
2761
2821
  if S.scoped_flags.get('overload_operators', False) and (start.value is '-' or start.value is '+' or start.value is '~'):
2762
2822
  ex.overloaded = True
2823
+ if S.scoped_flags.get('truthiness', False) and start.value is '!':
2824
+ ex.python_truthiness = True
2763
2825
  return ex
2764
2826
 
2765
2827
  val = expr_atom(allow_calls)
@@ -2795,7 +2857,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2795
2857
  'operator': op,
2796
2858
  'right': right,
2797
2859
  'end': right.end,
2798
- 'overloaded': S.scoped_flags.get('overload_operators', False)
2860
+ 'overloaded': S.scoped_flags.get('overload_operators', False),
2861
+ 'python_truthiness': S.scoped_flags.get('truthiness', False) and (op is '&&' or op is '||')
2799
2862
  })
2800
2863
  return expr_op(ret, min_prec, no_in)
2801
2864
  return left
@@ -2815,7 +2878,8 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2815
2878
  'condition': ne,
2816
2879
  'consequent': expr,
2817
2880
  'alternative': expression(False, no_in),
2818
- 'end': peek()
2881
+ 'end': peek(),
2882
+ 'python_truthiness': S.scoped_flags.get('truthiness', False)
2819
2883
  })
2820
2884
  return conditional
2821
2885
  return expr
package/src/tokenizer.pyj CHANGED
@@ -53,6 +53,7 @@ OPERATORS = make_predicate([
53
53
  "//=",
54
54
  "/=",
55
55
  "*=",
56
+ "**=",
56
57
  "%=",
57
58
  ">>=",
58
59
  "<<=",