rapydscript-ng 0.7.18 → 0.7.22

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.
Files changed (52) hide show
  1. package/.github/workflows/ci.yml +40 -0
  2. package/CHANGELOG.md +49 -1
  3. package/README.md +22 -7
  4. package/bin/web-repl-export +2 -2
  5. package/package.json +2 -2
  6. package/publish.py +9 -8
  7. package/release/baselib-plain-pretty.js +1707 -1044
  8. package/release/baselib-plain-ugly.js +3 -1
  9. package/release/compiler.js +5530 -3366
  10. package/release/signatures.json +18 -17
  11. package/src/ast.pyj +3 -1
  12. package/src/baselib-builtins.pyj +14 -3
  13. package/src/baselib-containers.pyj +13 -6
  14. package/src/baselib-internal.pyj +5 -1
  15. package/src/baselib-str.pyj +15 -2
  16. package/src/lib/elementmaker.pyj +2 -2
  17. package/src/lib/gettext.pyj +8 -7
  18. package/src/lib/random.pyj +1 -0
  19. package/src/lib/re.pyj +4 -3
  20. package/src/lib/traceback.pyj +16 -4
  21. package/src/output/classes.pyj +33 -5
  22. package/src/output/codegen.pyj +5 -38
  23. package/src/output/comments.pyj +45 -0
  24. package/src/output/functions.pyj +30 -19
  25. package/src/output/modules.pyj +19 -6
  26. package/src/output/operators.pyj +50 -19
  27. package/src/output/statements.pyj +2 -2
  28. package/src/output/stream.pyj +13 -21
  29. package/src/parse.pyj +52 -36
  30. package/src/string_interpolation.pyj +6 -1
  31. package/src/tokenizer.pyj +8 -2
  32. package/test/baselib.pyj +10 -0
  33. package/test/classes.pyj +24 -0
  34. package/test/collections.pyj +8 -0
  35. package/test/functions.pyj +16 -0
  36. package/test/generic.pyj +39 -1
  37. package/test/internationalization.pyj +8 -2
  38. package/test/regexp.pyj +3 -2
  39. package/test/scoped_flags.pyj +2 -0
  40. package/test/starargs.pyj +43 -0
  41. package/test/str.pyj +19 -8
  42. package/tools/cli.js +6 -1
  43. package/tools/compiler.js +3 -3
  44. package/tools/embedded_compiler.js +3 -3
  45. package/tools/export.js +3 -3
  46. package/tools/gettext.js +5 -2
  47. package/tools/lint.js +24 -13
  48. package/tools/msgfmt.js +1 -1
  49. package/web-repl/env.js +7 -1
  50. package/.appveyor.yml +0 -13
  51. package/.npmignore +0 -9
  52. package/.travis.yml +0 -12
@@ -3,11 +3,14 @@
3
3
  # globals: writefile
4
4
  from __python__ import hash_literals
5
5
 
6
- from parse import COMPILER_VERSION
7
- from utils import cache_file_name
8
6
  from output.statements import declare_vars, display_body
9
7
  from output.stream import OutputStream
10
8
  from output.utils import create_doctring
9
+ from output.comments import print_comments, output_comments
10
+ from output.functions import set_module_name
11
+ from parse import get_compiler_version
12
+ from utils import cache_file_name
13
+
11
14
 
12
15
  def write_imports(module, output):
13
16
  imports = []
@@ -116,7 +119,8 @@ def prologue(module, output):
116
119
 
117
120
 
118
121
  def print_top_level(self, output):
119
- is_main = output.is_main()
122
+ set_module_name(self.module_id)
123
+ is_main = self.module_id is '__main__'
120
124
 
121
125
  def write_docstrings():
122
126
  if is_main and output.options.keep_docstrings and self.docstrings and self.docstrings.length:
@@ -148,6 +152,10 @@ def print_top_level(self, output):
148
152
  display_body(self.body, True, output)
149
153
  output.newline()
150
154
  write_docstrings()
155
+ if self.comments_after and self.comments_after.length:
156
+ output.indent()
157
+ output_comments(self.comments_after, output)
158
+ output.newline()
151
159
  )
152
160
  )
153
161
  output.print("();")
@@ -164,8 +172,12 @@ def print_top_level(self, output):
164
172
 
165
173
  declare_vars(self.localvars, output)
166
174
  display_body(self.body, True, output)
175
+ if self.comments_after and self.comments_after.length:
176
+ output_comments(self.comments_after, output)
177
+ set_module_name()
167
178
 
168
179
  def print_module(self, output):
180
+ set_module_name(self.module_id)
169
181
 
170
182
  def output_module(output):
171
183
  declare_vars(self.localvars, output)
@@ -178,6 +190,7 @@ def print_module(self, output):
178
190
  output.print("function()")
179
191
  output.with_block(def():
180
192
  # dump the logic of this module
193
+ print_comments(self, output)
181
194
  if output.options.write_name:
182
195
  output.indent()
183
196
  output.print('var ')
@@ -194,9 +207,9 @@ def print_module(self, output):
194
207
  output_module(output)
195
208
  if self.srchash and self.filename:
196
209
  cached = {
197
- 'version':COMPILER_VERSION, 'signature':self.srchash, 'classes': {}, 'baselib':self.baselib,
210
+ 'version':get_compiler_version(), 'signature':self.srchash, 'classes': {}, 'baselib':self.baselib,
198
211
  'nonlocalvars':self.nonlocalvars, 'imported_module_ids':self.imported_module_ids, 'exports':[],
199
- 'outputs':{}, 'discard_asserts': output.options.discard_asserts
212
+ 'outputs':{}, 'discard_asserts': v'!!output.options.discard_asserts'
200
213
  }
201
214
  for cname in Object.keys(self.classes):
202
215
  cobj = self.classes[cname]
@@ -225,6 +238,7 @@ def print_module(self, output):
225
238
  output.print("()")
226
239
  output.semicolon()
227
240
  output.newline()
241
+ set_module_name()
228
242
 
229
243
  def print_imports(container, output):
230
244
  is_first_aname = True
@@ -246,7 +260,6 @@ def print_imports(container, output):
246
260
  output.end_statement()
247
261
 
248
262
  for self in container.imports:
249
- output.import_(self.module)
250
263
  if self.argnames:
251
264
  # A from import
252
265
  for argname in self.argnames:
@@ -1,14 +1,15 @@
1
1
  # vim:fileencoding=utf-8
2
2
  # License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
3
3
  from __python__ import hash_literals
4
-
5
4
  from ast import (
6
- AST_Number, AST_Unary, AST_Seq, AST_Array, AST_Binary, AST_Set, AST_Object,
7
- AST_Statement, AST_Conditional, AST_BaseCall, AST_Symbol, AST_SymbolRef, AST_Assign,
8
- AST_Return, AST_SimpleStatement, AST_String, AST_Sub, AST_ItemAccess, is_node_type
5
+ AST_Array, AST_Assign, AST_BaseCall, AST_Binary, AST_Conditional,
6
+ AST_ItemAccess, AST_Number, AST_Object, AST_Return, AST_Seq, AST_Set,
7
+ AST_SimpleStatement, AST_Statement, AST_String, AST_Sub, AST_Symbol,
8
+ AST_SymbolRef, AST_Unary, is_node_type
9
9
  )
10
10
  from output.loops import unpack_tuple
11
11
 
12
+
12
13
  def print_getattr(self, output, skip_expression): # AST_Dot
13
14
  if not skip_expression:
14
15
  expr = self.expression
@@ -54,7 +55,17 @@ def print_rich_getitem(self, output): # AST_ItemAccess
54
55
  output.print(func + '(')
55
56
  self.expression.print(output), output.comma(), self.property.print(output)
56
57
  if self.assignment:
57
- output.comma(), self.assignment.print(output)
58
+ output.comma()
59
+ asg = self.assignment
60
+ as_op = self.assign_operator
61
+ if as_op.length > 0:
62
+ self.assignment = None
63
+ print_rich_getitem(self, output)
64
+ self.assignment = asg
65
+ output.space()
66
+ output.print(as_op)
67
+ output.space()
68
+ self.assignment.print(output)
58
69
  output.print(')')
59
70
 
60
71
  def print_splice_assignment(self, output): # AST_Splice
@@ -82,7 +93,12 @@ def print_unary_prefix(self, output):
82
93
  output.print(op)
83
94
  if /^[a-z]/i.test(op):
84
95
  output.space()
85
- self.expression.print(output)
96
+ if self.parenthesized:
97
+ output.with_parens(def():
98
+ self.expression.print(output)
99
+ )
100
+ else:
101
+ self.expression.print(output)
86
102
 
87
103
  def write_instanceof(left, right, output):
88
104
 
@@ -123,19 +139,21 @@ def write_smart_equality(self, output):
123
139
  output.print('ρσ_' + ('equals(' if self.operator is '==' else 'not_equals('))
124
140
  self.left.print(output), output.print(','), output.space(), self.right.print(output), output.print(')')
125
141
 
126
- def print_binary_op(self, output):
127
- comparators = {
128
- "<": True,
129
- ">": True,
130
- "<=": True,
131
- ">=": True,
132
- "==": True,
133
- "!=": True
134
- }
135
- function_ops = {
136
- "in": "ρσ_in",
137
- }
138
142
 
143
+ comparators = {
144
+ "<": True,
145
+ ">": True,
146
+ "<=": True,
147
+ ">=": True,
148
+ }
149
+
150
+ function_ops = {
151
+ "in": "ρσ_in",
152
+ 'nin': '!ρσ_in',
153
+ }
154
+
155
+
156
+ def print_binary_op(self, output):
139
157
  if function_ops[self.operator]:
140
158
  output.print(function_ops[self.operator])
141
159
  output.with_parens(def():
@@ -144,6 +162,7 @@ def print_binary_op(self, output):
144
162
  self.right.print(output)
145
163
  )
146
164
  elif comparators[self.operator] and is_node_type(self.left, AST_Binary) and comparators[self.left.operator]:
165
+ # A chained comparison such as a < b < c
147
166
  if is_node_type(self.left.right, AST_Symbol):
148
167
  # left side compares against a regular variable,
149
168
  # no caching needed
@@ -184,7 +203,7 @@ def print_binary_op(self, output):
184
203
  if is_node_type(self.left, AST_Unary) and not self.left.parenthesized:
185
204
  left = self.left.expression
186
205
  output.print(self.left.operator)
187
- if output.options.js_version > 5:
206
+ if output.options.js_version > 6:
188
207
  output.print('(('), left.print(output), output.spaced(')', '**', '('), self.right.print(output), output.print('))')
189
208
  else:
190
209
  output.print('Math.pow('), left.print(output), output.comma(), self.right.print(output), output.print(')')
@@ -194,6 +213,18 @@ def print_binary_op(self, output):
194
213
  write_instanceof(self.left, self.right, output)
195
214
  elif self.operator is '*' and is_node_type(self.left, AST_String):
196
215
  self.left.print(output), output.print('.repeat('), self.right.print(output), output.print(')')
216
+ elif self.operator is '===' or self.operator is '!==':
217
+ nan_check = None
218
+ if is_node_type(self.right, AST_Symbol) and self.right.name is 'NaN':
219
+ nan_check = self.left
220
+ if is_node_type(self.left, AST_Symbol) and self.left.name is 'NaN':
221
+ nan_check = self.right
222
+ if nan_check is not None:
223
+ # We use the fact that NaN is the only object that is not equal to
224
+ # itself
225
+ output.spaced(nan_check, '!==' if self.operator is '===' else '===', nan_check)
226
+ else:
227
+ output.spaced(self.left, self.operator, self.right)
197
228
  else:
198
229
  output.spaced(self.left, self.operator, self.right)
199
230
 
@@ -112,7 +112,7 @@ def print_bracketed(node, output, complex, function_preamble, before, after):
112
112
  if complex:
113
113
  display_complex_body(node, False, output, function_preamble)
114
114
  else:
115
- display_body(node.body, False, output, function_preamble)
115
+ display_body(node.body, False, output)
116
116
  if after:
117
117
  after(output)
118
118
  )
@@ -171,7 +171,7 @@ def print_with(self, output):
171
171
  def print_assert(self, output):
172
172
  if output.options.discard_asserts:
173
173
  return
174
- output.spaced('if', '(!'), self.condition.print(output), output.spaced(')', 'throw new AssertionError')
174
+ output.spaced('if', '(!('), self.condition.print(output), output.spaced('))', 'throw new AssertionError')
175
175
  if self.message:
176
176
  output.print('(')
177
177
  self.message.print(output)
@@ -8,25 +8,27 @@ from tokenizer import is_identifier_char
8
8
 
9
9
  DANGEROUS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g
10
10
 
11
+
12
+ def as_hex(code, sz):
13
+ val = code.toString(16)
14
+ if val.length < sz:
15
+ val = '0'.repeat(sz - val.length) + val
16
+ return val
17
+
18
+
11
19
  def to_ascii(str_, identifier):
12
20
  return str_.replace(/[\u0080-\uffff]/g, def(ch):
13
21
  code = ch.charCodeAt(0).toString(16)
14
22
  if code.length <= 2 and not identifier:
15
- while code.length < 2:
16
- code = "0" + code
17
-
18
- return "\\x" + code
23
+ return "\\x" + as_hex(code, 2)
19
24
  else:
20
- while code.length < 4:
21
- code = "0" + code
22
-
23
- return "\\u" + code
25
+ return '\\u' + as_hex(code, 4)
24
26
 
25
27
  )
26
28
 
27
29
  def encode_string(str_):
28
30
  return JSON.stringify(str_).replace(DANGEROUS, def(a):
29
- return '\\u' + a.charCodeAt(0).toString(16)
31
+ return '\\u' + as_hex(a.charCodeAt(0), 4)
30
32
  )
31
33
  require_semi_colon_chars = make_predicate("( [ + * / - , .")
32
34
 
@@ -64,7 +66,6 @@ class OutputStream:
64
66
  self.current_line = 1
65
67
  self.current_pos = 0
66
68
  self.OUTPUT = ""
67
- self.IMPORTED = {}
68
69
  self.might_need_space = False
69
70
  self.might_need_semicolon = False
70
71
  self._last = None
@@ -249,7 +250,7 @@ class OutputStream:
249
250
  self.indent()
250
251
  self.spaced('var', 'ρσ_regenerator', '=', '{}')
251
252
  self.end_statement()
252
- code = regenerate(False, self.options.beautify)
253
+ code = 'ρσ_regenerator.regeneratorRuntime = ' + regenerate(False, self.options.beautify)
253
254
  if self.options.beautify:
254
255
  code = code.replace(/\/\/.*$/mg, '\n').replace(/^\s*$/gm, '') # strip comments
255
256
  ci = self.make_indent(0)
@@ -259,9 +260,7 @@ class OutputStream:
259
260
 
260
261
  def get(self):
261
262
  return self.OUTPUT
262
-
263
- def toString(self):
264
- return self.OUTPUT
263
+ toString = get
265
264
 
266
265
  def assign(self, name):
267
266
  # generates: '[name] = '
@@ -285,13 +284,6 @@ class OutputStream:
285
284
  def print_string(self, str_):
286
285
  self.print(encode_string(str_))
287
286
 
288
- def import_(self, module):
289
- if not Object.prototype.hasOwnProperty.call(self.IMPORTED, module.key):
290
- self.IMPORTED[module.key] = module
291
-
292
- def is_main(self):
293
- return self.OUTPUT.length is 0
294
-
295
287
  def line(self):
296
288
  return self.current_line
297
289
 
package/src/parse.pyj CHANGED
@@ -29,8 +29,17 @@ from tokenizer import tokenizer, is_token, RESERVED_WORDS
29
29
  COMPILER_VERSION = '__COMPILER_VERSION__'
30
30
  PYTHON_FLAGS = {'dict_literals':True, 'overload_getitem':True, 'bound_methods':True, 'hash_literals':True}
31
31
 
32
+
33
+ def get_compiler_version():
34
+ return COMPILER_VERSION
35
+
36
+
37
+ def static_predicate(names):
38
+ return {k:True for k in names.split(' ')}
39
+
32
40
  NATIVE_CLASSES = {
33
41
  'Image': {},
42
+ 'FileReader': {},
34
43
  'RegExp': {},
35
44
  'Error': {},
36
45
  'EvalError': {},
@@ -41,24 +50,26 @@ NATIVE_CLASSES = {
41
50
  'TypeError': {},
42
51
  'URIError': {},
43
52
  'Object': {
44
- 'static': [
45
- "getOwnPropertyNames", 'getOwnPropertyDescriptor',
46
- 'getOwnPropertySymbols', "keys", 'entries', 'values', "create", 'defineProperty',
47
- 'defineProperties', 'getPrototypeOf', 'setPrototypeOf', 'assign'
48
- ]
53
+ 'static': static_predicate(
54
+ 'getOwnPropertyNames getOwnPropertyDescriptor getOwnPropertyDescriptors'
55
+ ' getOwnPropertySymbols keys entries values create defineProperty'
56
+ ' defineProperties getPrototypeOf setPrototypeOf assign'
57
+ ' seal isSealed is preventExtensions isExtensible'
58
+ ' freeze isFrozen'
59
+ )
49
60
  },
50
61
  'String': {
51
- 'static': [ "fromCharCode" ]
62
+ 'static': static_predicate("fromCharCode")
52
63
  },
53
64
  'Array': {
54
- 'static': [ "isArray", "from", "of" ]
65
+ 'static': static_predicate("isArray from of")
55
66
  },
56
67
  'Function': {},
57
68
  'Date': {
58
- 'static': [ "UTC", "now", "parse" ]
69
+ 'static': static_predicate("UTC now parse")
59
70
  },
60
71
  'ArrayBuffer': {
61
- 'static': ['isView', 'transfer'],
72
+ 'static': static_predicate('isView transfer')
62
73
  },
63
74
  'DataView': {},
64
75
  'Float32Array': {},
@@ -76,7 +87,7 @@ NATIVE_CLASSES = {
76
87
  'Set': {},
77
88
  'WeakSet': {},
78
89
  'Promise': {
79
- 'static': ['all', 'race', 'reject', 'resolve']
90
+ 'static': static_predicate('all race reject resolve')
80
91
  },
81
92
  'WebSocket': {},
82
93
  'XMLHttpRequest': {},
@@ -97,7 +108,7 @@ ERROR_CLASSES = {
97
108
  'AssertionError': {},
98
109
  'ZeroDivisionError': {},
99
110
  }
100
- COMMON_STATIC = [ "call", "apply", "bind", "toString" ]
111
+ COMMON_STATIC = static_predicate('call apply bind toString')
101
112
  FORBIDDEN_CLASS_VARS = 'prototype constructor'.split(' ')
102
113
 
103
114
  # -----[ Parser (constants) ]-----
@@ -119,7 +130,7 @@ PRECEDENCE = (def(a, ret):
119
130
  [ "^" ],
120
131
  [ "&" ],
121
132
  [ "==", "===", "!=", "!==" ],
122
- [ "<", ">", "<=", ">=", "in", "instanceof" ],
133
+ [ "<", ">", "<=", ">=", "in", "nin", "instanceof" ],
123
134
  [ ">>", "<<", ">>>" ],
124
135
  [ "+", "-" ],
125
136
  [ "*", "/", "//", "%" ],
@@ -320,7 +331,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
320
331
  continue
321
332
 
322
333
  # recursive descent into conditional, loop and exception bodies
323
- for option in ('body', 'alternative', 'bcatch'):
334
+ for option in ('body', 'alternative', 'bcatch', 'condition'):
324
335
  opt = stmt[option]
325
336
  if opt:
326
337
  extend(scan_for_local_vars(opt))
@@ -467,13 +478,20 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
467
478
  expect(".")
468
479
  expect_token("keyword", "while")
469
480
  tmp = expression(True)
481
+ if is_node_type(tmp, AST_Assign):
482
+ croak('Assignments in do loop conditions are not allowed')
470
483
  semicolon()
471
484
  return tmp
472
485
  )()
473
486
  })
474
487
  elif tmp_ is "while":
488
+ while_cond = expression(True)
489
+ if is_node_type(while_cond, AST_Assign):
490
+ croak('Assignments in while loop conditions are not allowed')
491
+ if not is_('punc', ':'):
492
+ croak('Expected a colon after the while statement')
475
493
  return new AST_While({
476
- 'condition': expression(True),
494
+ 'condition': while_cond,
477
495
  'body': in_loop(statement)
478
496
  })
479
497
  elif tmp_ is "for":
@@ -739,7 +757,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
739
757
  cached = None
740
758
 
741
759
  srchash = sha1sum(src_code) # noqa:undef
742
- if cached and cached.version is COMPILER_VERSION and cached.signature is srchash and cached.discard_asserts is options.discard_asserts:
760
+ if cached and cached.version is COMPILER_VERSION and cached.signature is srchash and cached.discard_asserts is v'!!options.discard_asserts':
743
761
  for ikey in cached.imported_module_ids:
744
762
  do_import(ikey) # Ensure all modules imported by the cached module are also imported
745
763
  imported_modules[key] = {
@@ -880,7 +898,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
880
898
  externaldecorator = has_simple_decorator(S.decorators, 'external')
881
899
 
882
900
  class_details = {
883
- "static": [],
901
+ "static": {},
884
902
  'bound': v'[]',
885
903
  'classvars': {},
886
904
  'processing': name.name,
@@ -969,7 +987,9 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
969
987
  if is_node_type(node, AST_Method):
970
988
  class_var_names[node.name.name] = True
971
989
  return
972
- elif is_node_type(node, AST_Assign) and is_node_type(node.left, AST_SymbolRef):
990
+ if is_node_type(node, AST_Function):
991
+ return
992
+ if is_node_type(node, AST_Assign) and is_node_type(node.left, AST_SymbolRef):
973
993
  varname = node.left.name
974
994
  if FORBIDDEN_CLASS_VARS.indexOf(varname) is not -1:
975
995
  token_error(node.left.start, varname + ' is not allowed as a class variable name')
@@ -1002,7 +1022,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1002
1022
  if staticloc:
1003
1023
  if property_getter or property_setter:
1004
1024
  croak('A method cannot be both static and a property getter/setter')
1005
- S.classes[S.classes.length - 2][in_class].static.push(name.name)
1025
+ S.classes[S.classes.length - 2][in_class].static[name.name] = True
1006
1026
  staticmethod = True
1007
1027
  elif name.name is not "__init__" and S.scoped_flags.get('bound_methods'):
1008
1028
  S.classes[S.classes.length - 2][in_class].bound.push(name.name)
@@ -1689,7 +1709,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1689
1709
  return sym
1690
1710
 
1691
1711
  def is_static_method(cls, method):
1692
- if COMMON_STATIC.indexOf(method) is not -1 or cls.static and cls.static.indexOf(method) is not -1:
1712
+ if has_prop(COMMON_STATIC, method) or (cls.static and has_prop(cls.static, method)):
1693
1713
  return True
1694
1714
  else:
1695
1715
  return False
@@ -1782,7 +1802,9 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1782
1802
  # regular index (arr[index])
1783
1803
  if is_py_sub:
1784
1804
  assignment = None
1785
- if is_("operator") and S.token.value is "=":
1805
+ assign_operator = ''
1806
+ if is_("operator") and ASSIGNMENT[S.token.value]:
1807
+ assign_operator = S.token.value[:-1]
1786
1808
  next()
1787
1809
  assignment = expression(True)
1788
1810
  return subscripts(new AST_ItemAccess({
@@ -1792,6 +1814,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1792
1814
  'value': 0
1793
1815
  }),
1794
1816
  'assignment':assignment,
1817
+ 'assign_operator': assign_operator,
1795
1818
  'end': prev()
1796
1819
  }), allow_calls)
1797
1820
 
@@ -1941,10 +1964,11 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1941
1964
  return new AST_EmptyStatement({'stype':'@', 'start':prev(), 'end':prev()})
1942
1965
  if is_("operator") and UNARY_PREFIX[start.value]:
1943
1966
  next()
1967
+ is_parenthesized = is_('punc', '(')
1944
1968
  S.in_delete = start.value is 'delete'
1945
1969
  expr = maybe_unary(allow_calls)
1946
1970
  S.in_delete = False
1947
- ex = make_unary(AST_UnaryPrefix, start.value, expr)
1971
+ ex = make_unary(AST_UnaryPrefix, start.value, expr, is_parenthesized)
1948
1972
  ex.start = start
1949
1973
  ex.end = prev()
1950
1974
  return ex
@@ -1952,23 +1976,21 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1952
1976
  val = expr_atom(allow_calls)
1953
1977
  return val
1954
1978
 
1955
- def make_unary(ctor, op, expr):
1979
+ def make_unary(ctor, op, expr, is_parenthesized):
1956
1980
  return new ctor({
1957
1981
  'operator': op,
1958
- 'expression': expr
1982
+ 'expression': expr,
1983
+ 'parenthesized': is_parenthesized
1959
1984
  })
1960
1985
 
1961
1986
  def expr_op(left, min_prec, no_in):
1962
1987
  op = S.token.value if is_('operator') else None
1963
- not_in = False
1964
1988
  if op is "!" and peek().type is "operator" and peek().value is "in":
1965
1989
  next()
1966
- op = "in"
1967
- not_in = True
1990
+ S.token.value = op = 'nin'
1968
1991
 
1969
- if op is "in":
1970
- if no_in:
1971
- op = None
1992
+ if no_in and (op is "in" or op is 'nin'):
1993
+ op = None
1972
1994
 
1973
1995
  prec = PRECEDENCE[op] if op is not None else None
1974
1996
  if prec is not None and prec > min_prec:
@@ -1981,13 +2003,6 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1981
2003
  'right': right,
1982
2004
  'end': right.end
1983
2005
  })
1984
- if not_in:
1985
- ret = new AST_UnaryPrefix({
1986
- 'start': left.start,
1987
- 'operator': "!",
1988
- 'expression': ret,
1989
- 'end': right.end
1990
- })
1991
2006
  return expr_op(ret, min_prec, no_in)
1992
2007
  return left
1993
2008
 
@@ -2166,6 +2181,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
2166
2181
  toplevel.baselib = baselib_items
2167
2182
  toplevel.scoped_flags = S.scoped_flags.stack[0]
2168
2183
  importing_modules[module_id] = False
2184
+ toplevel.comments_after = S.token.comments_before or v'[]'
2169
2185
  return toplevel
2170
2186
 
2171
2187
  return run_parser
@@ -14,7 +14,12 @@ def render_markup(markup):
14
14
  key += ch
15
15
  pos += 1
16
16
  fmtspec = markup[pos:]
17
- return 'ρσ_str.format("{' + fmtspec + '}", ' + key + ')'
17
+ prefix = ''
18
+ if key.endsWith('='):
19
+ prefix=key
20
+ key = key[:-1]
21
+ return 'ρσ_str.format("' + prefix + '{' + fmtspec + '}", ' + key + ')'
22
+
18
23
 
19
24
  def interpolate(template, raise_error):
20
25
  pos = in_brace = 0
package/src/tokenizer.pyj CHANGED
@@ -458,7 +458,10 @@ def tokenizer(raw_text, filename):
458
458
  else:
459
459
  return token(tok_type, '')
460
460
 
461
- while ch = next(True, True):
461
+ while True:
462
+ ch = next(True, True)
463
+ if not ch:
464
+ break
462
465
  if ch is "\n" and not is_multiline:
463
466
  parse_error("End of line while scanning string literal")
464
467
 
@@ -529,7 +532,10 @@ def tokenizer(raw_text, filename):
529
532
  else: # empty regexp (//)
530
533
  mods = read_name()
531
534
  return token("regexp", RegExp(regexp, mods))
532
- while (ch = next(True)):
535
+ while True:
536
+ ch = next(True)
537
+ if not ch:
538
+ break
533
539
  if in_comment:
534
540
  if ch is '\n':
535
541
  in_comment = False
package/test/baselib.pyj CHANGED
@@ -20,6 +20,11 @@ assrt.deepEqual(['a', 'b'], list('ab'))
20
20
  assrt.ok(q is not list(q))
21
21
  assrt.deepEqual(q, list(q))
22
22
  assrt.ok(isinstance([], (String, list)))
23
+ assrt.ok(isinstance(1, int))
24
+ assrt.ok(not isinstance(v'new Number(1)', int))
25
+ assrt.ok(not isinstance(1.1, int))
26
+ assrt.ok(isinstance(1.1, float))
27
+ assrt.ok(not isinstance(v'new Number(1.1)', float))
23
28
  m = Map()
24
29
  m.set('a', 1)
25
30
  assrt.equal(len(m), 1)
@@ -70,6 +75,9 @@ assrt.equal(int('a', 16), 10)
70
75
  assrt.throws(def ():int('a');, ValueError)
71
76
  assrt.equal(float('10.3'), 10.3)
72
77
  assrt.throws(def ():float('a');, ValueError)
78
+ assrt.equal(int(2*1e-7), 0)
79
+ assrt.equal(int(10*1e-7), 0)
80
+ assrt.equal(float(3*1e-7), 3e-7)
73
81
 
74
82
  # sum()
75
83
  assrt.equal(6, sum([1, 2, 3]))
@@ -259,6 +267,8 @@ assrt.equal(b == a, False)
259
267
  a, b = range(1111111111)
260
268
  assrt.equal(a, 0)
261
269
  assrt.equal(b, 1)
270
+ assrt.equal(len(range(10)), 10)
271
+ assrt.equal(str(range(10)), 'range(0, 10, 1)')
262
272
 
263
273
  assrt.deepEqual(divmod(7, 3), [2, 1])
264
274
  assrt.deepEqual(divmod(-7, 3), [-3, 2])
package/test/classes.pyj CHANGED
@@ -401,6 +401,7 @@ class B2(B1):
401
401
  def two(self):
402
402
  return self.b2
403
403
  assrt.equal(B2().two(), 2)
404
+ assrt.equal(B2().b1, 1)
404
405
 
405
406
 
406
407
  class Cvar:
@@ -426,3 +427,26 @@ assrt.equal(Cvar.a, 2)
426
427
  assrt.equal(Cvar().val(), 3)
427
428
  assrt.equal(Cvar().incb(), 1)
428
429
  assrt.equal(Cvar().incb(), 2)
430
+
431
+ class anon_func_in_class:
432
+
433
+ f = def():
434
+ func_var = 1
435
+ return func_var
436
+
437
+ def func_var(self):
438
+ pass
439
+
440
+ assrt.equal(jstype(anon_func_in_class.prototype.func_var), 'function')
441
+ anon_func_in_class.prototype.f()
442
+ assrt.equal(jstype(anon_func_in_class.prototype.func_var), 'function')
443
+
444
+
445
+ def decorate(cls):
446
+ assrt.equal(cls.prototype.somevar, 1)
447
+ return cls
448
+
449
+
450
+ @decorate
451
+ class decorated:
452
+ somevar = 1
@@ -69,6 +69,13 @@ assrt.deepEqual(a, [3,2,1])
69
69
  a.pysort()
70
70
  assrt.deepEqual(a, [1,2,3])
71
71
 
72
+ # misc interface
73
+ a = [1,2,3]
74
+ assrt.equal(a.pypop(), 3)
75
+ assrt.deepEqual(a, [1,2])
76
+ assrt.equal(a.pypop(0), 1)
77
+ assrt.deepEqual(a, [2])
78
+
72
79
  # strings
73
80
  assrt.ok("tes" in "this is a test")
74
81
 
@@ -104,6 +111,7 @@ assrt.equal(d1.fun2(3), 4)
104
111
  assrt.equal(len(d0), 3)
105
112
  assrt.equal(d2.get(1), 2)
106
113
  assrt.equal(d2.get('a'), 'b')
114
+ assrt.deepEqual(d2.popitem(), ['a', 'b'])
107
115
 
108
116
  # assignment
109
117
  d1["bar"] += "!"