rapydscript-ng 0.7.21 → 0.7.23

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.
@@ -712,7 +712,6 @@ if (!ρσ_list_extend.__argnames__) Object.defineProperties(ρσ_list_extend, {
712
712
  });
713
713
 
714
714
  function ρσ_list_index(val, start, stop) {
715
- var idx;
716
715
  start = start || 0;
717
716
  if (start < 0) {
718
717
  start = this.length + start;
@@ -721,11 +720,7 @@ function ρσ_list_index(val, start, stop) {
721
720
  throw new ValueError(val + " is not in list");
722
721
  }
723
722
  if (stop === undefined) {
724
- idx = this.indexOf(val, start);
725
- if (idx === -1) {
726
- throw new ValueError(val + " is not in list");
727
- }
728
- return idx;
723
+ stop = this.length;
729
724
  }
730
725
  if (stop < 0) {
731
726
  stop = this.length + stop;
@@ -762,12 +757,13 @@ if (!ρσ_list_pop.__argnames__) Object.defineProperties(ρσ_list_pop, {
762
757
  });
763
758
 
764
759
  function ρσ_list_remove(value) {
765
- var idx;
766
- idx = this.indexOf(value);
767
- if (idx === -1) {
768
- throw new ValueError(value + " not in list");
760
+ for (var i = 0; i < this.length; i++) {
761
+ if (((ρσ_expr_temp = this)[(typeof i === "number" && i < 0) ? ρσ_expr_temp.length + i : i] === value || typeof (ρσ_expr_temp = this)[(typeof i === "number" && i < 0) ? ρσ_expr_temp.length + i : i] === "object" && ρσ_equals((ρσ_expr_temp = this)[(typeof i === "number" && i < 0) ? ρσ_expr_temp.length + i : i], value))) {
762
+ this.splice(i, 1);
763
+ return;
764
+ }
769
765
  }
770
- this.splice(idx, 1);
766
+ throw new ValueError(value + " not in list");
771
767
  };
772
768
  if (!ρσ_list_remove.__argnames__) Object.defineProperties(ρσ_list_remove, {
773
769
  __argnames__ : {value: ["value"]},
@@ -2008,7 +2004,7 @@ Object.defineProperties(ρσ_dict.prototype, (function(){
2008
2004
  });
2009
2005
  return ρσ_anonfunc;
2010
2006
  })();
2011
- ρσ_dict.prototype.set_default = (function() {
2007
+ ρσ_dict.prototype.set_default = ρσ_dict.prototype.setdefault = (function() {
2012
2008
  var ρσ_anonfunc = function (key, defval) {
2013
2009
  var j;
2014
2010
  j = this.jsmap;
@@ -2072,13 +2068,20 @@ Object.defineProperties(ρσ_dict.prototype, (function(){
2072
2068
  })();
2073
2069
  ρσ_dict.prototype.popitem = (function() {
2074
2070
  var ρσ_anonfunc = function () {
2075
- var r;
2076
- r = this.jsmap.entries().next();
2077
- if (r.done) {
2078
- throw new KeyError("dict is empty");
2071
+ var last, e, r;
2072
+ last = null;
2073
+ e = this.jsmap.entries();
2074
+ while (true) {
2075
+ r = e.next();
2076
+ if (r.done) {
2077
+ if (last === null) {
2078
+ throw new KeyError("dict is empty");
2079
+ }
2080
+ this.jsmap.delete(last.value[0]);
2081
+ return last.value;
2082
+ }
2083
+ last = r;
2079
2084
  }
2080
- this.jsmap.delete(r.value[0]);
2081
- return r.value;
2082
2085
  };
2083
2086
  if (!ρσ_anonfunc.__module__) Object.defineProperties(ρσ_anonfunc, {
2084
2087
  __module__ : {value: "__main__"}
@@ -2703,6 +2706,7 @@ function ρσ_setitem(obj, key, val) {
2703
2706
  }
2704
2707
  obj[(typeof key === "number" && key < 0) ? obj.length + key : key] = val;
2705
2708
  }
2709
+ return val;
2706
2710
  };
2707
2711
  if (!ρσ_setitem.__argnames__) Object.defineProperties(ρσ_setitem, {
2708
2712
  __argnames__ : {value: ["obj", "key", "val"]},
@@ -7090,6 +7094,7 @@ return this.__repr__();
7090
7094
  AST_ItemAccess.prototype.properties = (function(){
7091
7095
  var ρσ_d = Object.create(null);
7092
7096
  ρσ_d["assignment"] = "[AST_Node or None] Not None if this is an assignment (a[x] = y) rather than a simple access";
7097
+ ρσ_d["assign_operator"] = "[String] The operator for a assignment like += or empty string if plain assignment";
7093
7098
  return ρσ_d;
7094
7099
  }).call(this);
7095
7100
 
@@ -8601,6 +8606,7 @@ return this.__repr__();
8601
8606
  var SyntaxError = ρσ_modules.errors.SyntaxError;
8602
8607
 
8603
8608
  var interpolate = ρσ_modules.string_interpolation.interpolate;
8609
+ var quoted_string = ρσ_modules.string_interpolation.quoted_string;
8604
8610
 
8605
8611
  RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
8606
8612
  RE_OCT_NUMBER = /^0[0-7]+$/;
@@ -9247,6 +9253,7 @@ return this.__repr__();
9247
9253
  return ρσ_anonfunc;
9248
9254
  })());
9249
9255
  function handle_interpolated_string(string, start_tok) {
9256
+ var parts, ch, stok, j, potential_mod, mods, is_raw, combined;
9250
9257
  function raise_error(err) {
9251
9258
  throw new SyntaxError(err, filename, start_tok.line, start_tok.col, start_tok.pos, false);
9252
9259
  };
@@ -9255,7 +9262,50 @@ return this.__repr__();
9255
9262
  __module__ : {value: "tokenizer"}
9256
9263
  });
9257
9264
 
9258
- S.text = S.text.slice(0, S.pos) + "(" + interpolate(string, raise_error) + ")" + S.text.slice(S.pos);
9265
+ parts = [interpolate(string, raise_error)];
9266
+ while (true) {
9267
+ while (S.pos < S.text.length && (S.text.charAt(S.pos) === " " || S.text.charAt(S.pos) === "\t")) {
9268
+ next();
9269
+ }
9270
+ ch = S.text.charAt(S.pos);
9271
+ if (!ch) {
9272
+ break;
9273
+ }
9274
+ if (ch === "'" || ch === "\"") {
9275
+ stok = read_string(false, false);
9276
+ parts.push(quoted_string(stok.value));
9277
+ } else if (is_identifier_start(ch.charCodeAt(0))) {
9278
+ j = S.pos;
9279
+ while (j < S.text.length && is_identifier_char(S.text.charAt(j))) {
9280
+ j += 1;
9281
+ }
9282
+ potential_mod = S.text.substring(S.pos, j);
9283
+ if (!is_string_modifier(potential_mod)) {
9284
+ break;
9285
+ }
9286
+ if (j >= S.text.length || "'\"".indexOf(S.text.charAt(j)) === -1) {
9287
+ break;
9288
+ }
9289
+ mods = potential_mod.toLowerCase();
9290
+ if (mods.indexOf("v") !== -1) {
9291
+ break;
9292
+ }
9293
+ while (S.pos < j) {
9294
+ next();
9295
+ }
9296
+ is_raw = mods.indexOf("r") !== -1;
9297
+ stok = read_string(is_raw, false);
9298
+ if (mods.indexOf("f") !== -1) {
9299
+ parts.push(interpolate(stok.value, raise_error));
9300
+ } else {
9301
+ parts.push(quoted_string(stok.value));
9302
+ }
9303
+ } else {
9304
+ break;
9305
+ }
9306
+ }
9307
+ combined = parts.join("+");
9308
+ S.text = S.text.slice(0, S.pos) + "(" + combined + ")" + S.text.slice(S.pos);
9259
9309
  return token("punc", next());
9260
9310
  };
9261
9311
  if (!handle_interpolated_string.__argnames__) Object.defineProperties(handle_interpolated_string, {
@@ -9646,7 +9696,7 @@ return this.__repr__();
9646
9696
  var is_token = ρσ_modules.tokenizer.is_token;
9647
9697
  var RESERVED_WORDS = ρσ_modules.tokenizer.RESERVED_WORDS;
9648
9698
 
9649
- COMPILER_VERSION = "eb857dad74098b17e1abd5c68b2fed35a74ea3d0";
9699
+ COMPILER_VERSION = "33bc40d444e2a542a0577a3a3b0549d8381795ff";
9650
9700
  PYTHON_FLAGS = (function(){
9651
9701
  var ρσ_d = Object.create(null);
9652
9702
  ρσ_d["dict_literals"] = true;
@@ -12207,7 +12257,7 @@ return this.__repr__();
12207
12257
  });
12208
12258
 
12209
12259
  function getitem(expr, allow_calls) {
12210
- var start, is_py_sub, slice_bounds, is_slice, i, assignment;
12260
+ var start, is_py_sub, slice_bounds, is_slice, i, assignment, assign_operator;
12211
12261
  start = expr.start;
12212
12262
  next();
12213
12263
  is_py_sub = S.scoped_flags.get("overload_getitem", false);
@@ -12326,7 +12376,9 @@ return this.__repr__();
12326
12376
  } else {
12327
12377
  if (is_py_sub) {
12328
12378
  assignment = null;
12329
- if (is_("operator") && S.token.value === "=") {
12379
+ assign_operator = "";
12380
+ if (is_("operator") && ASSIGNMENT[ρσ_bound_index(S.token.value, ASSIGNMENT)]) {
12381
+ assign_operator = S.token.value.slice(0, -1);
12330
12382
  next();
12331
12383
  assignment = expression(true);
12332
12384
  }
@@ -12340,6 +12392,7 @@ return this.__repr__();
12340
12392
  return ρσ_d;
12341
12393
  }).call(this));
12342
12394
  ρσ_d["assignment"] = assignment;
12395
+ ρσ_d["assign_operator"] = assign_operator;
12343
12396
  ρσ_d["end"] = prev();
12344
12397
  return ρσ_d;
12345
12398
  }).call(this)), allow_calls);
@@ -14984,12 +15037,23 @@ return this.__repr__();
14984
15037
  });
14985
15038
 
14986
15039
  function print_rich_getitem(self, output) {
14987
- var func;
15040
+ var func, asg, as_op;
14988
15041
  func = "ρσ_" + ((self.assignment) ? "setitem" : "getitem");
14989
15042
  output.print(func + "(");
14990
15043
  [self.expression.print(output), output.comma(), self.property.print(output)];
14991
15044
  if (self.assignment) {
14992
- [output.comma(), self.assignment.print(output)];
15045
+ output.comma();
15046
+ asg = self.assignment;
15047
+ as_op = self.assign_operator;
15048
+ if (as_op.length > 0) {
15049
+ self.assignment = null;
15050
+ print_rich_getitem(self, output);
15051
+ self.assignment = asg;
15052
+ output.space();
15053
+ output.print(as_op);
15054
+ output.space();
15055
+ }
15056
+ self.assignment.print(output);
14993
15057
  }
14994
15058
  output.print(")");
14995
15059
  };
@@ -15356,7 +15420,7 @@ return this.__repr__();
15356
15420
  var ρσ_Iter89 = ρσ_Iterable(left_hand_sides);
15357
15421
  for (var ρσ_Index89 = 0; ρσ_Index89 < ρσ_Iter89.length; ρσ_Index89++) {
15358
15422
  lhs = ρσ_Iter89[ρσ_Index89];
15359
- if (is_node_type(lhs, AST_Seq) || is_node_type(lhs, AST_Array)) {
15423
+ if (is_node_type(lhs, AST_Seq) || is_node_type(lhs, AST_Array) || is_node_type(lhs, AST_ItemAccess)) {
15360
15424
  is_compound_assign = true;
15361
15425
  break;
15362
15426
  }
@@ -1,9 +1,9 @@
1
1
  {
2
- "ast": "81fefe5999ac0ce23d87f05861a113f21e776714",
2
+ "ast": "7a1a1800d9ee7af7f7153eee38002e4e9dcfb71e",
3
3
  "baselib-builtins": "50927b38fded9bd9dbe14c42a20dd8e930ac021f",
4
- "baselib-containers": "92a0841862b0f8b22a3748081a2223d3405cfa41",
4
+ "baselib-containers": "59936060f92bff9c83a70489276f7e2f6edb6630",
5
5
  "baselib-errors": "f2423e1e2489bdd7fb8c8725bb76436898c8008c",
6
- "baselib-internal": "1b77d40970a8ef2d886132dc7c21c9510d2eb4cd",
6
+ "baselib-internal": "7a8544ec19d35ac9f6cea9bf1a8584b105d648b2",
7
7
  "baselib-itertools": "ab1f3257642c8d26fab1ff9d392814a459a60cdb",
8
8
  "baselib-str": "8c396e4fae02ae34b6d3d805becf7bfdd36625ee",
9
9
  "compiler": "cfbfceb0439fed3302f78d80b303dca713b25e7b",
@@ -17,15 +17,15 @@
17
17
  "output/literals": "fdbfb744fec8dfea1e1c62cb49767c60160fd402",
18
18
  "output/loops": "f59cc623320f57edb01953f7877d712758e9f825",
19
19
  "output/modules": "bd3e3f3f6794fb6e401867b4d011c4e3aab98893",
20
- "output/operators": "4d1c1cd4d7da71a3ccdd802add3c88e35bb92d06",
20
+ "output/operators": "bcda6aeab9214523252481c8dba2741c01f725bb",
21
21
  "output/statements": "7d8dfdc0b43e3e430be7ce3b23ffaf1b41408d8c",
22
22
  "output/stream": "46375cb467477ac898546e818bfdfa5be738c352",
23
23
  "output/utils": "595968f96f6fdcc51eb41c34d64c92bb595e3cb1",
24
- "parse": "b63fd10695f03eecbfa8e02887e6783ba87d7be3",
24
+ "parse": "d5c1a77662dfcff841dfcd368d5dbb129a592f5d",
25
25
  "string_interpolation": "bff1cc76d772d24d35707f6c794f38734ca08376",
26
- "tokenizer": "00fc24a41edac9f9b31735e6290348005a623cef",
26
+ "tokenizer": "1b23dc17814989686f4dc5b69f776b127f63c520",
27
27
  "unicode_aliases": "79ac6eaa5e6be44a5397d62c561f854a8fe7528e",
28
28
  "utils": "c1666db819aa7c8db38697e34e18362e3cb45869",
29
- "#compiler#": "00bd8ed07a7de8d6bf5e6f380f831d0ac00713e8",
30
- "#compiled_with#": "00bd8ed07a7de8d6bf5e6f380f831d0ac00713e8"
29
+ "#compiler#": "0b52100610a7474259047753e409de52cdb3e12c",
30
+ "#compiled_with#": "0b52100610a7474259047753e409de52cdb3e12c"
31
31
  }
package/src/ast.pyj CHANGED
@@ -719,6 +719,7 @@ class AST_ItemAccess(AST_PropAccess):
719
719
  'Python index-style property access, i.e. `a.__getitem__("foo")`'
720
720
  properties = {
721
721
  'assignment': "[AST_Node or None] Not None if this is an assignment (a[x] = y) rather than a simple access",
722
+ 'assign_operator': "[String] The operator for a assignment like += or empty string if plain assignment",
722
723
  }
723
724
 
724
725
  def _walk(self, visitor):
@@ -67,10 +67,7 @@ def ρσ_list_index(val, start, stop):
67
67
  if start < 0:
68
68
  raise ValueError(val + ' is not in list')
69
69
  if stop is undefined:
70
- idx = this.indexOf(val, start)
71
- if idx is -1:
72
- raise ValueError(val + ' is not in list')
73
- return idx
70
+ stop = this.length
74
71
  if stop < 0:
75
72
  stop = this.length + stop
76
73
  for v'var i = start; i < stop; i++':
@@ -89,10 +86,11 @@ def ρσ_list_pop(index):
89
86
  return ans[0]
90
87
 
91
88
  def ρσ_list_remove(value):
92
- idx = this.indexOf(value)
93
- if idx is -1:
94
- raise ValueError(value + ' not in list')
95
- this.splice(idx, 1)
89
+ for v'var i = 0; i < this.length; i++':
90
+ if this[i] == value:
91
+ this.splice(i, 1)
92
+ return
93
+ raise ValueError(value + ' not in list')
96
94
 
97
95
  def ρσ_list_to_string():
98
96
  return '[' + this.join(', ') + ']'
@@ -606,7 +604,7 @@ Object.defineProperties(ρσ_dict.prototype, {
606
604
  return None if defval is undefined else defval
607
605
  return ans
608
606
 
609
- ρσ_dict.prototype.set_default = def (key, defval):
607
+ ρσ_dict.prototype.set_default = ρσ_dict.prototype.setdefault = def (key, defval):
610
608
  j = this.jsmap
611
609
  if not j.has(key):
612
610
  j.set(key, defval)
@@ -632,11 +630,16 @@ Object.defineProperties(ρσ_dict.prototype, {
632
630
  return ans
633
631
 
634
632
  ρσ_dict.prototype.popitem = def ():
635
- r = this.jsmap.entries().next()
636
- if r.done:
637
- raise KeyError('dict is empty')
638
- this.jsmap.delete(r.value[0])
639
- return r.value
633
+ last = None
634
+ e = this.jsmap.entries()
635
+ while True:
636
+ r = e.next()
637
+ if r.done:
638
+ if last is None:
639
+ raise KeyError('dict is empty')
640
+ this.jsmap.delete(last.value[0])
641
+ return last.value
642
+ last = r
640
643
 
641
644
  ρσ_dict.prototype.update = def ():
642
645
  if arguments.length is 0:
@@ -175,6 +175,7 @@ def ρσ_setitem(obj, key, val):
175
175
  if jstype(key) is 'number' and key < 0:
176
176
  key += obj.length
177
177
  obj[key] = val
178
+ return val
178
179
 
179
180
  def ρσ_delitem(obj, key):
180
181
  if obj.__delitem__:
package/src/lib/math.pyj CHANGED
@@ -17,6 +17,7 @@
17
17
 
18
18
  pi = Math.PI
19
19
  e = Math.E
20
+ inf = Infinity
20
21
 
21
22
  ########################################
22
23
  # Number-theoretic and representation functions
@@ -51,9 +51,30 @@ def random():
51
51
  m *= 256
52
52
  return v'n / 0x10000000000000000'
53
53
 
54
- # unlike the python version, this DOES build a range object, feel free to reimplement
55
54
  def randrange():
56
- return choice(range.apply(this, arguments))
55
+ if arguments.length is 1:
56
+ return randbelow(int(arguments[0]))
57
+ start = int(arguments[0])
58
+ stop = int(arguments[1])
59
+ if arguments.length < 3:
60
+ step = 1
61
+ else:
62
+ step = int(arguments[2])
63
+ width = stop - start
64
+ if step is 1:
65
+ if width > 0:
66
+ return start + randbelow(width)
67
+ raise ValueError("empty range for randrange()")
68
+ if step > 0:
69
+ n = (width + step - 1) // step
70
+ elif step < 0:
71
+ n = (width + step + 1) // step
72
+ else:
73
+ raise ValueError("zero step for randrange()")
74
+ if n <= 0:
75
+ raise ValueError(f"empty range in randrange({start}, {stop}, {step})")
76
+ return start + step * randbelow(n)
77
+
57
78
 
58
79
  def randint(a, b):
59
80
  return int(random()*(b-a+1) + a)
@@ -61,9 +82,12 @@ def randint(a, b):
61
82
  def uniform(a, b):
62
83
  return random()*(b-a) + a
63
84
 
85
+ def randbelow(n):
86
+ return Math.floor(random()*n)
87
+
64
88
  def choice(seq):
65
89
  if seq.length > 0:
66
- return seq[Math.floor(random()*seq.length)]
90
+ return seq[randbelow(seq.length)]
67
91
  else:
68
92
  raise IndexError()
69
93
 
@@ -55,7 +55,17 @@ def print_rich_getitem(self, output): # AST_ItemAccess
55
55
  output.print(func + '(')
56
56
  self.expression.print(output), output.comma(), self.property.print(output)
57
57
  if self.assignment:
58
- 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)
59
69
  output.print(')')
60
70
 
61
71
  def print_splice_assignment(self, output): # AST_Splice
@@ -286,7 +296,7 @@ def print_assign(self, output):
286
296
  left_hand_sides, rhs = self.traverse_chain()
287
297
  is_compound_assign = False
288
298
  for lhs in left_hand_sides:
289
- if is_node_type(lhs, AST_Seq) or is_node_type(lhs, AST_Array):
299
+ if is_node_type(lhs, AST_Seq) or is_node_type(lhs, AST_Array) or is_node_type(lhs, AST_ItemAccess):
290
300
  is_compound_assign = True
291
301
  break
292
302
  if is_compound_assign:
package/src/parse.pyj CHANGED
@@ -1802,7 +1802,9 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1802
1802
  # regular index (arr[index])
1803
1803
  if is_py_sub:
1804
1804
  assignment = None
1805
- 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]
1806
1808
  next()
1807
1809
  assignment = expression(True)
1808
1810
  return subscripts(new AST_ItemAccess({
@@ -1812,6 +1814,7 @@ def create_parser_ctx(S, import_dirs, module_id, baselib_items, imported_module_
1812
1814
  'value': 0
1813
1815
  }),
1814
1816
  'assignment':assignment,
1817
+ 'assign_operator': assign_operator,
1815
1818
  'end': prev()
1816
1819
  }), allow_calls)
1817
1820
 
package/src/tokenizer.pyj CHANGED
@@ -6,7 +6,7 @@ from unicode_aliases import ALIAS_MAP
6
6
  from utils import make_predicate, characters
7
7
  from ast import AST_Token
8
8
  from errors import SyntaxError
9
- from string_interpolation import interpolate
9
+ from string_interpolation import interpolate, quoted_string
10
10
 
11
11
  RE_HEX_NUMBER = /^0x[0-9a-f]+$/i
12
12
  RE_OCT_NUMBER = /^0[0-7]+$/
@@ -486,7 +486,45 @@ def tokenizer(raw_text, filename):
486
486
  def handle_interpolated_string(string, start_tok):
487
487
  def raise_error(err):
488
488
  raise new SyntaxError(err, filename, start_tok.line, start_tok.col, start_tok.pos, False)
489
- S.text = S.text[:S.pos] + '(' + interpolate(string, raise_error) + ')' + S.text[S.pos:]
489
+ parts = v'[interpolate(string, raise_error)]'
490
+ # Look ahead for consecutive string literals to concatenate (e.g. f'a'f'b' or f'a''b')
491
+ while True:
492
+ # Skip horizontal whitespace (spaces and tabs, not newlines)
493
+ while S.pos < S.text.length and (S.text.charAt(S.pos) is ' ' or S.text.charAt(S.pos) is '\t'):
494
+ next()
495
+ ch = S.text.charAt(S.pos)
496
+ if not ch:
497
+ break
498
+ if ch is "'" or ch is '"':
499
+ # A plain string literal follows; read it and append its quoted value
500
+ stok = read_string(False, False)
501
+ parts.push(quoted_string(stok.value))
502
+ elif is_identifier_start(ch.charCodeAt(0)):
503
+ # Peek ahead (without consuming) to check if it is a string modifier followed by a quote
504
+ j = S.pos
505
+ while j < S.text.length and is_identifier_char(S.text.charAt(j)):
506
+ j += 1
507
+ potential_mod = S.text.substring(S.pos, j)
508
+ if not is_string_modifier(potential_mod):
509
+ break
510
+ if j >= S.text.length or '\'"'.indexOf(S.text.charAt(j)) is -1:
511
+ break
512
+ mods = potential_mod.toLowerCase()
513
+ if mods.indexOf('v') is not -1:
514
+ break # Do not concatenate with verbatim JS literals
515
+ # Consume the modifier characters via next() to keep position tracking correct
516
+ while S.pos < j:
517
+ next()
518
+ is_raw = mods.indexOf('r') is not -1
519
+ stok = read_string(is_raw, False)
520
+ if mods.indexOf('f') is not -1:
521
+ parts.push(interpolate(stok.value, raise_error))
522
+ else:
523
+ parts.push(quoted_string(stok.value))
524
+ else:
525
+ break
526
+ combined = parts.join('+')
527
+ S.text = S.text[:S.pos] + '(' + combined + ')' + S.text[S.pos:]
490
528
  return token('punc', next())
491
529
 
492
530
  def read_line_comment(shebang):
package/test/baselib.pyj CHANGED
@@ -59,6 +59,14 @@ assrt.deepEqual(['y', 'x'], [x for x in reversed('xy')])
59
59
  # Test that the iterator created by iter() is itself iterable
60
60
  assrt.deepEqual(s, set(iter(s)))
61
61
 
62
+ # Test list index/remove/in use the same notion of equality
63
+ a = [1, 2]
64
+ b = [list(a), 3, 4]
65
+ assrt.ok(a in b)
66
+ assrt.equal(b.index(a), 0)
67
+ b.remove(a)
68
+ assrt.equal(len(b), 2)
69
+
62
70
  assrt.ok('a' in m)
63
71
  assrt.ok(1 in s)
64
72
  assrt.ok('1' not in s)
@@ -234,6 +242,8 @@ def test_dicts():
234
242
  assrt.ok(a is not a.copy()), assrt.deepEqual(a, a.copy())
235
243
  a.clear()
236
244
  assrt.equal(a.length, 0), assrt.deepEqual(list(a), [])
245
+ a[1] = a[2] = 19
246
+ assrt.deepEqual(a, {1:19, 2:19})
237
247
  assrt.deepEqual(set({1:9, 2:8}.keys()), {1, 2})
238
248
  assrt.deepEqual(set({1:9, 2:8}.values()), {8, 9})
239
249
  items = [list_wrap(x) for x in {1:9, 2:8}.items()]
@@ -241,8 +251,8 @@ def test_dicts():
241
251
  assrt.deepEqual(items, [[1,9], [2, 8]])
242
252
  a = {1:1, 2:2}
243
253
  assrt.equal(a.get(1), 1), assrt.equal(a.get(3), None)
244
- assrt.equal(a.set_default(2, 2), 2)
245
- assrt.equal(a.set_default(3, 3), 3), assrt.equal(a[3], 3)
254
+ assrt.equal(a.setdefault(2, 2), 2)
255
+ assrt.equal(a.setdefault(3, 3), 3), assrt.equal(a[3], 3)
246
256
  assrt.deepEqual(dict.fromkeys([1, 2], 3), {1:3, 2:3})
247
257
  a = {1:3, 2:3}
248
258
  assrt.equal(a.pop(2, 2), 3), assrt.equal(a.pop(2, 2), 2)
@@ -111,6 +111,7 @@ assrt.equal(d1.fun2(3), 4)
111
111
  assrt.equal(len(d0), 3)
112
112
  assrt.equal(d2.get(1), 2)
113
113
  assrt.equal(d2.get('a'), 'b')
114
+ assrt.deepEqual(d2.popitem(), ['a', 'b'])
114
115
 
115
116
  # assignment
116
117
  d1["bar"] += "!"
@@ -10,6 +10,8 @@ assrt.ok(isinstance(a, dict))
10
10
  assrt.equal(a[1], 1)
11
11
  a[2] = 2
12
12
  assrt.equal(a[2], 2)
13
+ a[2] += 4
14
+ assrt.equal(a[2], 6)
13
15
  assrt.deepEqual(list(a.keys()), [1, 2])
14
16
  from __python__ import no_dict_literals, no_overload_getitem
15
17
 
package/test/str.pyj CHANGED
@@ -49,6 +49,14 @@ def test_interpolation():
49
49
  ae(f'{a=}', 'a=1')
50
50
  somevar = {'x': 1}
51
51
  ae(f'{somevar.x=}', 'somevar.x=1')
52
+ # Test consecutive f-string concatenation
53
+ ae(f'a'f'b', 'ab')
54
+ ae(f'a' f'b', 'ab')
55
+ ae(f'a'f'{a}', 'a1')
56
+ ae(f'{a}' f'b', '1b')
57
+ ae(f'{a}'f'{a}', '11')
58
+ ae(f'a''b', 'ab')
59
+ ae(f'{a}''b', '1b')
52
60
 
53
61
  somevar = 33
54
62
  test('somevar=33', '{somevar=}', somevar=somevar)
package/tools/cli.js CHANGED
@@ -350,7 +350,7 @@ it.
350
350
  create_group('lint', "[input1.pyj input2.pyj ...]", function(){/*
351
351
  Run the RapydScript linter. This will find various
352
352
  possible problems in the .pyj files you specify and
353
- write messages about them to stdout.
353
+ write messages about them to stdout. Use - to read from STDIN.
354
354
  The main check it performs is for unused/undefined
355
355
  symbols, like pyflakes does for python.
356
356
  */}, function() {/*
@@ -421,6 +421,11 @@ List all available linter checks, with a brief
421
421
  description, and exit.
422
422
  */});
423
423
 
424
+ opt('stdin_filename', '', 'string', 'STDIN', function(){/*
425
+ The filename for data read from STDIN. If not specified
426
+ STDIN is used.
427
+ */});
428
+
424
429
  create_group('test', '[test1 test2...]', function(){/*
425
430
  Run RapydScript tests. You can specify the name of
426
431
  individual test files to only run tests from those
package/tools/lint.js CHANGED
@@ -564,7 +564,7 @@ function lint_code(code, options) {
564
564
  // CLI {{{
565
565
 
566
566
  function read_whole_file(filename, cb) {
567
- if (!filename) {
567
+ if (!filename || filename === '-') {
568
568
  var chunks = [];
569
569
  process.stdin.setEncoding('utf-8');
570
570
  process.stdin.on('data', function (chunk) {
@@ -653,6 +653,10 @@ module.exports.cli = function(argv, base_path, src_path, lib_path) {
653
653
  if (argv.globals) argv.globals.split(',').forEach(function(sym) { builtins[sym] = true; });
654
654
  if (argv.noqa) argv.noqa.split(',').forEach(function(sym) { noqa[sym] = true; });
655
655
 
656
+ function path_for_filename(x) {
657
+ return x === '-' ? argv.stdin_filename : x;
658
+ }
659
+
656
660
  function lint_single_file(err, code) {
657
661
  var output, final_builtins = merge(builtins), final_noqa = merge(noqa), rl;
658
662
  if (err) {
@@ -661,7 +665,7 @@ module.exports.cli = function(argv, base_path, src_path, lib_path) {
661
665
  }
662
666
 
663
667
  // Read setup.cfg
664
- rl = get_ini(path.dirname(files[0]));
668
+ rl = get_ini(path.dirname(path_for_filename(files[0])));
665
669
  var g = {};
666
670
  (rl.globals || rl.builtins || '').split(',').forEach(function (x) { g[x.trim()] = true; });
667
671
  final_builtins = merge(final_builtins, g);
@@ -681,7 +685,7 @@ module.exports.cli = function(argv, base_path, src_path, lib_path) {
681
685
  });
682
686
 
683
687
  // Lint!
684
- if (lint_code(code, {filename:files[0], builtins:final_builtins, noqa:final_noqa, errorformat:argv.errorformat || false}).length) all_ok = false;
688
+ if (lint_code(code, {filename:path_for_filename(files[0]), builtins:final_builtins, noqa:final_noqa, errorformat:argv.errorformat || false}).length) all_ok = false;
685
689
 
686
690
  files = files.slice(1);
687
691
  if (files.length) {