rapydscript-ng 0.8.2 → 0.8.3

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "ast": "83fe570708f4a25ee630120ec1d2a1f47b39bd2f",
2
+ "ast": "6c918899920124616fcf87f30c2db475bf05d7b4",
3
3
  "baselib-builtins": "0e170c877a1bcc67e506df0ba64037bf1a2f6121",
4
4
  "baselib-containers": "39d05f99eb7cc741e44871ccc6acea868ee70ec8",
5
5
  "baselib-errors": "7f786439249e7d06cc010757f1bbaa6a2c3ea019",
@@ -13,9 +13,9 @@
13
13
  "output/codegen": "ff6f50e4533271e30ca55aff80c18f244d1c0f22",
14
14
  "output/comments": "b3baa47c00c1983ee54e912c602b58318e9c2bde",
15
15
  "output/exceptions": "7b9548eab7d67991c4932746a0ce766e07e9ac8d",
16
- "output/functions": "a213dd5b806410c6575a78488cf813b4e8ddb1e9",
16
+ "output/functions": "1a88ea89a36366d958d0a6dcf5d3477775197702",
17
17
  "output/literals": "50a034dbdf6fca16de7b5147a7fd64faeb461255",
18
- "output/loops": "47a2767193ba5f996808ee05927844a551cf022b",
18
+ "output/loops": "567b34e2f81c92512482cdbdfa59e90c5cb1ea38",
19
19
  "output/modules": "e2c2d92c714c31face3470de7ae8a61275a95fcd",
20
20
  "output/operators": "f45450104ed5def1298cf9a4962492ffeca02e63",
21
21
  "output/statements": "a56a432dee96d9ed0e490fc024ed8a99d30a5d0c",
@@ -23,9 +23,9 @@
23
23
  "output/utils": "97837ab9091710059e2ba01e245ee05fec82cd29",
24
24
  "parse": "0965e0416ea81d2b4bce422729b662022c0c4eb4",
25
25
  "string_interpolation": "9cc04ded68b82174728873f7df96aed60a4fb478",
26
- "tokenizer": "4b38255101868598070367bcde7a11aea0c18276",
26
+ "tokenizer": "3c70e79fbe95b1f2d3164da4c96274b0d352d533",
27
27
  "unicode_aliases": "79ac6eaa5e6be44a5397d62c561f854a8fe7528e",
28
28
  "utils": "0cbca1fa76442212e31644f95a4f6e9a0317234f",
29
- "#compiler#": "adbd3a6d4a70a98f08be2be192009164746efc68",
30
- "#compiled_with#": "adbd3a6d4a70a98f08be2be192009164746efc68"
29
+ "#compiler#": "b3aec6fc48ac89b11c2c190d5fc03c3e688ede69",
30
+ "#compiled_with#": "b3aec6fc48ac89b11c2c190d5fc03c3e688ede69"
31
31
  }
package/src/ast.pyj CHANGED
@@ -129,15 +129,13 @@ class AST_Directive(AST_Statement):
129
129
  'Represents a directive, like "use strict";'
130
130
  properties = {
131
131
  'value': "[string] The value of this directive as a plain string (it's not an AST_String!)",
132
- 'scope': '[AST_Scope/S] The scope that this directive affects'
132
+ 'scope': '[AST_Scope/S] The scope that this directive affects',
133
133
  }
134
134
 
135
135
 
136
136
  class AST_SimpleStatement(AST_Statement):
137
137
  'A statement consisting of an expression, i.e. a = 1 + 2'
138
- properties = {
139
- 'body': '[AST_Node] an expression node (should not be instanceof AST_Statement)'
140
- }
138
+ properties = {'body': '[AST_Node] an expression node (should not be instanceof AST_Statement)'}
141
139
 
142
140
  def _walk(self, visitor):
143
141
  return visitor._visit(self, def ():
@@ -196,9 +194,7 @@ class AST_EmptyStatement(AST_Statement):
196
194
 
197
195
  class AST_StatementWithBody(AST_Statement):
198
196
  'Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`'
199
- properties = {
200
- 'body': "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
201
- }
197
+ properties = {'body': "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"}
202
198
 
203
199
  def _walk(self, visitor):
204
200
  return visitor._visit(self, def ():
@@ -208,9 +204,7 @@ class AST_StatementWithBody(AST_Statement):
208
204
 
209
205
  class AST_DWLoop(AST_StatementWithBody):
210
206
  'Base class for do/while statements'
211
- properties = {
212
- 'condition': '[AST_Node] the loop condition. Should not be instanceof AST_Statement'
213
- }
207
+ properties = {'condition': '[AST_Node] the loop condition. Should not be instanceof AST_Statement'}
214
208
 
215
209
  def _walk(self, visitor):
216
210
  return visitor._visit(self, def ():
@@ -256,7 +250,7 @@ class AST_ListComprehension(AST_ForIn):
256
250
  'A list comprehension expression'
257
251
  properties = {
258
252
  'condition': '[AST_Node] the `if` condition',
259
- 'statement': '[AST_Node] statement to perform on each element before returning it'
253
+ 'statement': '[AST_Node] statement to perform on each element before returning it',
260
254
  }
261
255
 
262
256
  def _walk(self, visitor):
@@ -498,9 +492,7 @@ class AST_Jump(AST_Statement):
498
492
 
499
493
  class AST_Exit(AST_Jump):
500
494
  'Base class for “exits” (`return` and `throw`)'
501
- properties = {
502
- 'value': '[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return'
503
- }
495
+ properties = {'value': '[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return'}
504
496
 
505
497
  def _walk(self, visitor):
506
498
  return visitor._visit(self, def ():
@@ -555,7 +547,7 @@ class AST_If(AST_StatementWithBody):
555
547
  'A `if` statement'
556
548
  properties = {
557
549
  'condition': '[AST_Node] the `if` condition',
558
- 'alternative': '[AST_Statement?] the `else` part, or null if not present'
550
+ 'alternative': '[AST_Statement?] the `else` part, or null if not present',
559
551
  }
560
552
 
561
553
  def _walk(self, visitor):
@@ -840,7 +832,7 @@ class AST_Splice(AST_PropAccess):
840
832
  'Index-style property access, i.e. `a[3:5]`'
841
833
  properties = {
842
834
  'property2': '[AST_Node] the 2nd property to access - typically ending index for the array.',
843
- 'assignment': '[AST_Node] The data being spliced in.'
835
+ 'assignment': '[AST_Node] The data being spliced in.',
844
836
  }
845
837
 
846
838
  def _walk(self, visitor):
@@ -919,10 +911,10 @@ class AST_Assign(AST_Binary):
919
911
  'An assignment expression — `a = b + 5`'
920
912
 
921
913
  def is_chained(self):
922
- return is_node_type(self.right, AST_Assign) or (
923
- is_node_type(self.right, AST_Seq) and (
924
- is_node_type(self.right.car, AST_Assign) or is_node_type(self.right.cdr, AST_Assign))
925
- )
914
+ return is_node_type(
915
+ self.right,
916
+ AST_Assign
917
+ ) or (is_node_type(self.right, AST_Seq) and (is_node_type(self.right.car, AST_Assign) or is_node_type(self.right.cdr, AST_Assign)))
926
918
 
927
919
  def traverse_chain(self):
928
920
  right = self.right
@@ -1056,7 +1048,7 @@ class AST_Symbol(AST_Node):
1056
1048
  properties = {
1057
1049
  'name': '[string] name of this symbol',
1058
1050
  'scope': '[AST_Scope/S] the current scope (not necessarily the definition scope)',
1059
- 'thedef': '[SymbolDef/S] the definition of this symbol'
1051
+ 'thedef': '[SymbolDef/S] the definition of this symbol',
1060
1052
  }
1061
1053
 
1062
1054
 
@@ -1088,9 +1080,7 @@ class AST_SymbolNonlocal(AST_SymbolDeclaration):
1088
1080
 
1089
1081
  class AST_SymbolFunarg(AST_SymbolVar):
1090
1082
  'Symbol naming a function argument, possibly with an annotation.'
1091
- properties = {
1092
- 'annotation': '[AST_Node?] The annotation provided for this argument (if any)'
1093
- }
1083
+ properties = {'annotation': '[AST_Node?] The annotation provided for this argument (if any)'}
1094
1084
 
1095
1085
 
1096
1086
  class AST_SymbolDefun(AST_SymbolDeclaration):
@@ -1242,7 +1232,10 @@ class TreeWalker:
1242
1232
  if is_node_type(p, AST_If) and p.condition is self
1243
1233
  or is_node_type(p, AST_Conditional) and p.condition is self
1244
1234
  or is_node_type(p, AST_DWLoop) and p.condition is self
1245
- or is_node_type(p, AST_UnaryPrefix) and p.operator is '!' and p.expression is self:
1235
+ or is_node_type(
1236
+ p,
1237
+ AST_UnaryPrefix
1238
+ ) and p.operator is '!' and p.expression is self:
1246
1239
  return True
1247
1240
  if not (is_node_type(p, AST_Binary) and (p.operator is '&&' or p.operator is '||')):
1248
1241
  return False
@@ -5,7 +5,6 @@ from __python__ import hash_literals
5
5
  from ast import AST_ClassCall, AST_New, has_calls, AST_Dot, AST_SymbolRef, is_node_type
6
6
  from output.operators import print_getattr
7
7
  from output.statements import print_bracketed
8
- from output.stream import OutputStream
9
8
  from output.utils import create_doctring
10
9
 
11
10
  anonfunc = 'ρσ_anonfunc'
@@ -63,14 +62,59 @@ def function_preamble(node, output, offset):
63
62
  output.space()
64
63
  output.assign(arg)
65
64
  if Object.prototype.hasOwnProperty.call(a.defaults, arg.name):
66
- output.spaced('(arguments[' + i + ']', '===', 'undefined', '||',
67
- '(', i, '===', 'arguments.length-1', '&&', kw, '!==', 'null', '&&', 'typeof', kw, '===', '"object"', '&&',
68
- kw, '[ρσ_kwargs_symbol]', '===', 'true))', '?', '')
65
+ output.spaced(
66
+ '(arguments[' + i + ']',
67
+ '===',
68
+ 'undefined',
69
+ '||',
70
+ '(',
71
+ i,
72
+ '===',
73
+ 'arguments.length-1',
74
+ '&&',
75
+ kw,
76
+ '!==',
77
+ 'null',
78
+ '&&',
79
+ 'typeof',
80
+ kw,
81
+ '===',
82
+ '"object"',
83
+ '&&',
84
+ kw,
85
+ '[ρσ_kwargs_symbol]',
86
+ '===',
87
+ 'true))',
88
+ '?',
89
+ ''
90
+ )
69
91
  output.print(fname + '.__defaults__.'), arg.print(output)
70
92
  output.space(), output.print(':'), output.space()
71
93
  else:
72
- output.spaced('(', i, '===', 'arguments.length-1', '&&', kw, '!==', 'null', '&&', 'typeof', kw, '===', '"object"', '&&',
73
- kw, '[ρσ_kwargs_symbol]', '===', 'true)', '?', 'undefined', ':', '')
94
+ output.spaced(
95
+ '(',
96
+ i,
97
+ '===',
98
+ 'arguments.length-1',
99
+ '&&',
100
+ kw,
101
+ '!==',
102
+ 'null',
103
+ '&&',
104
+ 'typeof',
105
+ kw,
106
+ '===',
107
+ '"object"',
108
+ '&&',
109
+ kw,
110
+ '[ρσ_kwargs_symbol]',
111
+ '===',
112
+ 'true)',
113
+ '?',
114
+ 'undefined',
115
+ ':',
116
+ ''
117
+ )
74
118
  output.print('arguments[' + i + ']')
75
119
  output.end_statement()
76
120
  if a.kwargs or a.has_defaults:
@@ -81,13 +125,35 @@ def function_preamble(node, output, offset):
81
125
  output.end_statement()
82
126
  # Ensure kwargs is the options object
83
127
  output.indent()
84
- output.spaced('if', '(' + kw, '===', 'null', '||', 'typeof', kw, '!==', '"object"', '||', kw, '[ρσ_kwargs_symbol]', '!==', 'true)', kw, '=', '{}')
128
+ output.spaced(
129
+ 'if',
130
+ '(' + kw,
131
+ '===',
132
+ 'null',
133
+ '||',
134
+ 'typeof',
135
+ kw,
136
+ '!==',
137
+ '"object"',
138
+ '||',
139
+ kw,
140
+ '[ρσ_kwargs_symbol]',
141
+ '!==',
142
+ 'true)',
143
+ kw,
144
+ '=',
145
+ '{}'
146
+ )
85
147
  output.end_statement()
86
148
  # Read values from the kwargs object for any formal parameters
87
149
  if a.has_defaults:
88
150
  for dname in Object.keys(a.defaults):
89
151
  output.indent()
90
- output.spaced('if', '(Object.prototype.hasOwnProperty.call(' + kw + ',', '"' + dname + '"))')
152
+ output.spaced(
153
+ 'if',
154
+ '(Object.prototype.hasOwnProperty.call(' + kw + ',',
155
+ '"' + dname + '"))'
156
+ )
91
157
  output.with_block(def ():
92
158
  output.indent()
93
159
  output.spaced(dname, '=', kw + '.' + dname)
@@ -103,11 +169,33 @@ def function_preamble(node, output, offset):
103
169
  # Define the *args parameter, putting in whatever is left after assigning the formal parameters and the options object
104
170
  nargs = a.args.length - offset
105
171
  output.indent()
106
- output.spaced('var', a.starargs.name, '=', 'Array.prototype.slice.call(arguments,', nargs + ')')
172
+ output.spaced(
173
+ 'var',
174
+ a.starargs.name,
175
+ '=',
176
+ 'Array.prototype.slice.call(arguments,',
177
+ nargs + ')'
178
+ )
107
179
  output.end_statement()
108
180
  # Remove the options object, if present
109
181
  output.indent()
110
- output.spaced('if', '(' + kw, '!==', 'null', '&&', 'typeof', kw, '===', '"object"', '&&', kw, '[ρσ_kwargs_symbol]', '===', 'true)', a.starargs.name)
182
+ output.spaced(
183
+ 'if',
184
+ '(' + kw,
185
+ '!==',
186
+ 'null',
187
+ '&&',
188
+ 'typeof',
189
+ kw,
190
+ '===',
191
+ '"object"',
192
+ '&&',
193
+ kw,
194
+ '[ρσ_kwargs_symbol]',
195
+ '===',
196
+ 'true)',
197
+ a.starargs.name
198
+ )
111
199
  output.print('.pop()')
112
200
  output.end_statement()
113
201
 
@@ -183,7 +271,11 @@ def function_annotation(self, output, strip_first, name):
183
271
  output.indent()
184
272
  # Only define the properties if they were not already defined, which
185
273
  # can happen if the function definition is inside a loop, for example.
186
- output.spaced('if', '(!' + fname + '.' + names[0] + ')', 'Object.defineProperties(' + fname)
274
+ output.spaced(
275
+ 'if',
276
+ '(!' + fname + '.' + names[0] + ')',
277
+ 'Object.defineProperties(' + fname
278
+ )
187
279
  output.comma()
188
280
  output.with_block(def ():
189
281
  for v'var i = 0; i < names.length; i++':
@@ -235,7 +327,10 @@ def function_definition(self, output, strip_first, as_expression):
235
327
  if as_expression:
236
328
  output.end_statement()
237
329
  function_annotation(self, output, strip_first, anonfunc)
238
- output.indent(), output.spaced('return', anonfunc), output.end_statement()
330
+ output.indent(), output.spaced(
331
+ 'return',
332
+ anonfunc
333
+ ), output.end_statement()
239
334
  output.set_indentation(orig_indent)
240
335
  output.indent(), output.print('})()')
241
336
 
@@ -386,7 +481,10 @@ def print_function_call(self, output):
386
481
 
387
482
  is_repeatable = is_new or not has_calls(self.expression)
388
483
  if not is_repeatable:
389
- output.assign('(ρσ_expr_temp'), print_this(self.expression, output), output.comma()
484
+ output.assign('(ρσ_expr_temp'), print_this(
485
+ self.expression,
486
+ output
487
+ ), output.comma()
390
488
 
391
489
  if has_kwargs:
392
490
  if is_new:
@@ -3,7 +3,6 @@
3
3
  from __python__ import hash_literals
4
4
 
5
5
  from ast import AST_BaseCall, AST_SymbolRef, AST_Array, AST_Unary, AST_Number, has_calls, AST_Seq, AST_ListComprehension, is_node_type
6
- from output.stream import OutputStream
7
6
 
8
7
 
9
8
  def unpack_tuple(elems, output, in_statement):
@@ -55,10 +54,7 @@ def is_simple_for(self):
55
54
  not (is_node_type(self.init, AST_Array))):
56
55
  a = self.object.args.args
57
56
  l = a.length
58
- if l < 3 or (
59
- is_node_type(a[2].value, AST_Number) or (
60
- is_node_type(a[2].value, AST_Unary) and a[2].value.operator is '-' and is_node_type(a[2].value.expression, AST_Number)
61
- )):
57
+ if l < 3 or (is_node_type(a[2].value, AST_Number) or (is_node_type(a[2].value, AST_Unary) and a[2].value.operator is '-' and is_node_type(a[2].value.expression, AST_Number))):
62
58
  if (l is 1 and not has_calls(a[0].value)) or (l > 1 and not has_calls(a[1].value)):
63
59
  return True
64
60
  return False
@@ -99,8 +95,24 @@ def print_for_loop_body(output):
99
95
 
100
96
  def init_es6_itervar(output, itervar):
101
97
  output.indent()
102
- output.spaced(itervar, '=', '((typeof', itervar + '[Symbol.iterator]', '===', '"function")', '?',
103
- '(' + itervar, 'instanceof', 'Map', '?', itervar + '.keys()', ':', itervar + ')', ':', 'Object.keys(' + itervar + '))')
98
+ output.spaced(
99
+ itervar,
100
+ '=',
101
+ '((typeof',
102
+ itervar + '[Symbol.iterator]',
103
+ '===',
104
+ '"function")',
105
+ '?',
106
+ '(' + itervar,
107
+ 'instanceof',
108
+ 'Map',
109
+ '?',
110
+ itervar + '.keys()',
111
+ ':',
112
+ itervar + ')',
113
+ ':',
114
+ 'Object.keys(' + itervar + '))'
115
+ )
104
116
  output.end_statement()
105
117
 
106
118
 
@@ -176,7 +188,13 @@ def print_for_in(self, output):
176
188
  output.end_statement()
177
189
  init_es6_itervar(output, itervar)
178
190
  output.indent()
179
- output.spaced('for', '(var', 'ρσ_Index' + output.index_counter, 'of', itervar + ')')
191
+ output.spaced(
192
+ 'for',
193
+ '(var',
194
+ 'ρσ_Index' + output.index_counter,
195
+ 'of',
196
+ itervar + ')'
197
+ )
180
198
 
181
199
  output.space()
182
200
  self._do_print_body(output)
@@ -184,7 +202,11 @@ def print_for_in(self, output):
184
202
 
185
203
  def print_list_comprehension(self, output):
186
204
  tname = self.constructor.name.slice(4)
187
- result_obj = {'ListComprehension': '[]', 'DictComprehension': ('Object.create(null)' if self.is_jshash else '{}'), 'SetComprehension': 'ρσ_set()'}[tname]
205
+ result_obj = {
206
+ 'ListComprehension': '[]',
207
+ 'DictComprehension': ('Object.create(null)' if self.is_jshash else '{}'),
208
+ 'SetComprehension': 'ρσ_set()',
209
+ }[tname]
188
210
  is_generator = tname is 'GeneratorComprehension'
189
211
  if tname is 'DictComprehension':
190
212
  if self.is_pydict:
package/src/tokenizer.pyj CHANGED
@@ -503,9 +503,15 @@ def tokenizer(raw_text, filename, recover_errors):
503
503
  parts = v'[quoted_string(string)]'
504
504
  # Look ahead for consecutive string literals to concatenate (e.g. f'a'f'b' or f'a''b')
505
505
  while True:
506
- # Skip horizontal whitespace (spaces and tabs, not newlines)
507
- while S.pos < S.text.length and (S.text.charAt(S.pos) is ' ' or S.text.charAt(S.pos) is '\t'):
508
- next()
506
+ # Skip horizontal whitespace; also skip newlines inside brackets where indentation is irrelevant
507
+ while S.pos < S.text.length:
508
+ wch = S.text.charAt(S.pos)
509
+ if wch is ' ' or wch is '\t':
510
+ next()
511
+ elif (wch is '\n' or wch is '\r') and not S.indentation_matters[-1]:
512
+ next()
513
+ else:
514
+ break
509
515
  ch = S.text.charAt(S.pos)
510
516
  if not ch:
511
517
  break
@@ -676,8 +682,14 @@ def tokenizer(raw_text, filename, recover_errors):
676
682
  # otherwise be parsed as a call expression "a"(...) after the tokenizer
677
683
  # injects the f-string result as a parenthesised expression.
678
684
  j = S.pos
679
- while j < S.text.length and (S.text.charAt(j) is ' ' or S.text.charAt(j) is '\t'):
680
- j += 1
685
+ while j < S.text.length:
686
+ jch = S.text.charAt(j)
687
+ if jch is ' ' or jch is '\t':
688
+ j += 1
689
+ elif (jch is '\n' or jch is '\r') and not S.indentation_matters[-1]:
690
+ j += 1
691
+ else:
692
+ break
681
693
  if j < S.text.length and is_identifier_start(S.text.charAt(j).charCodeAt(0)):
682
694
  k = j
683
695
  while k < S.text.length and is_identifier_char(S.text.charAt(k)):
package/test/str.pyj CHANGED
@@ -65,6 +65,16 @@ def test_interpolation():
65
65
  ae('a' f'{a}' 'b', 'a1b')
66
66
  ae('a' f'{a}' f'{a}', 'a11')
67
67
  ae(rf'raw{a}' f' and {a}', 'raw1 and 1')
68
+ # Test f-string + string concatenation across newlines inside brackets
69
+ ae(f'{a}'
70
+ "b", '1b')
71
+ ae(f'{a}'
72
+ f'{a}', '11')
73
+ ae('a'
74
+ f'{a}', 'a1')
75
+ ae(f'{a}'
76
+ "b"
77
+ f'{a}', '1b1')
68
78
 
69
79
  somevar = 33
70
80
  test('somevar=33', '{somevar=}', somevar=somevar)
package/tools/cli.mjs CHANGED
@@ -318,11 +318,12 @@ command prompt.`);
318
318
  opt("no_js", '', 'bool', false, `Do not display the compiled JavaScript before executing
319
319
  it.`);
320
320
 
321
- create_group('lint', "[input1.pyj input2.pyj ...]", `Run the RapydScript linter. This will find various
321
+ create_group('lint', "[input1.pyj dir1 ...]", `Run the RapydScript linter. This will find various
322
322
  possible problems in the .pyj files you specify and
323
323
  write messages about them to stdout. Use - to read from STDIN.
324
- The main check it performs is for unused/undefined
325
- symbols, like pyflakes does for python.`,
324
+ You can specify directories to recursively lint all .pyj
325
+ files within them. The main check it performs is for
326
+ unused/undefined symbols, like pyflakes does for python.`,
326
327
  `In addition to the command line options listed below,
327
328
  you can also control the linter in a couple of other ways.
328
329
 
package/tools/compile.mjs CHANGED
@@ -111,6 +111,12 @@ export default async function(start_time, argv, base_path, src_path, lib_path) {
111
111
  });
112
112
  }
113
113
 
114
+ function write_to_stream(stream, data) {
115
+ return new Promise((resolve, reject) => {
116
+ stream.write(data + '\n', 'utf8', err => err ? reject(err) : resolve());
117
+ });
118
+ }
119
+
114
120
  async function write_output(js_output, output_stream) {
115
121
  if (argv.source_map && output_stream) {
116
122
  var segments = output_stream.get_source_map_segments();
@@ -123,11 +129,11 @@ export default async function(start_time, argv, base_path, src_path, lib_path) {
123
129
  }
124
130
  if (argv.output) {
125
131
  // Node's filesystem module cannot write directly to /dev/stdout
126
- if (argv.output == '/dev/stdout') console.log(js_output);
127
- else if (argv.output == '/dev/stderr') console.error(js_output);
132
+ if (argv.output == '/dev/stdout') await write_to_stream(process.stdout, js_output);
133
+ else if (argv.output == '/dev/stderr') await write_to_stream(process.stderr, js_output);
128
134
  else await fs.promises.writeFile(argv.output, js_output, "utf8");
129
135
  } else if (!argv.execute){
130
- console.log(js_output);
136
+ await write_to_stream(process.stdout, js_output);
131
137
  }
132
138
  if (argv.execute) {
133
139
  vm.runInNewContext(js_output, {'console':console, 'require':require}, {'filename':files[0]});
@@ -0,0 +1,101 @@
1
+ /* vim:fileencoding=utf-8
2
+ *
3
+ * Copyright (C) 2015 Kovid Goyal <kovid at kovidgoyal.net>
4
+ *
5
+ * Distributed under terms of the BSD license
6
+ *
7
+ * Worker thread for parallel linting. Each worker owns its own compiler
8
+ * instance so parsing can run on multiple CPU cores simultaneously.
9
+ */
10
+
11
+ import { parentPort } from 'worker_threads';
12
+ import { create_compiler } from './compiler.mjs';
13
+ import { read_config } from './ini.mjs';
14
+ import * as utils from './utils.mjs';
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+
18
+ const merge = utils.merge;
19
+ const has_prop = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
20
+
21
+ // Initialize the compiler first — lint.mjs reads globalThis.create_rapydscript_compiler
22
+ // at module evaluation time, so the global must be set before the dynamic import.
23
+ const compiler = await create_compiler();
24
+ globalThis.create_rapydscript_compiler = () => compiler;
25
+
26
+ // Safe to import now.
27
+ const { lint_code } = await import('./lint.mjs');
28
+
29
+ // Per-worker ini cache keyed by directory. Stores promises to deduplicate
30
+ // concurrent lookups for the same directory.
31
+ const ini_cache = {};
32
+
33
+ function get_ini(dir) {
34
+ if (!has_prop(ini_cache, dir)) {
35
+ ini_cache[dir] = read_config(dir).then(r => r.rapydscript || {});
36
+ }
37
+ return ini_cache[dir];
38
+ }
39
+
40
+ // Signal to the main thread that this worker is ready to receive jobs.
41
+ parentPort.postMessage({ type: 'ready' });
42
+
43
+ parentPort.on('message', async (msg) => {
44
+ if (msg.type !== 'lint') return;
45
+ const { id, filename, base_builtins, base_noqa } = msg;
46
+
47
+ try {
48
+ const code = await fs.promises.readFile(filename, 'utf-8');
49
+
50
+ const final_builtins = merge(base_builtins);
51
+ const final_noqa = merge(base_noqa);
52
+
53
+ const rl = await get_ini(path.dirname(filename));
54
+ const g = {};
55
+ (rl.globals || rl.builtins || '').split(',').forEach(function(x) { g[x.trim()] = true; });
56
+ Object.assign(final_builtins, g);
57
+
58
+ const ng = {};
59
+ (rl.noqa || '').split(',').forEach(function(x) { ng[x.trim()] = true; });
60
+ Object.assign(final_noqa, ng);
61
+
62
+ code.split('\n', 20).forEach(function(line) {
63
+ var lq = line.replace(/\s+/g, '');
64
+ if (lq.startsWith('#globals:')) {
65
+ (lq.split(':', 2)[1] || '').split(',').forEach(function(item) { final_builtins[item] = true; });
66
+ } else if (lq.startsWith('#noqa:')) {
67
+ (lq.split(':', 2)[1] || '').split(',').forEach(function(item) { final_noqa[item] = true; });
68
+ }
69
+ });
70
+
71
+ // Suppress per-file output; the main thread reports in file order.
72
+ const messages = await lint_code(code, {
73
+ filename,
74
+ builtins: final_builtins,
75
+ noqa: final_noqa,
76
+ errorformat: false,
77
+ report: function() {},
78
+ });
79
+
80
+ // Strip code_lines — it is never used by the CLI report functions and
81
+ // transferring it across threads would be wasteful.
82
+ const clean = messages.map(function(m) {
83
+ return {
84
+ filename: m.filename,
85
+ start_line: m.start_line,
86
+ start_col: m.start_col,
87
+ end_line: m.end_line,
88
+ end_col: m.end_col,
89
+ ident: m.ident,
90
+ message: m.message,
91
+ level: m.level,
92
+ name: m.name,
93
+ other_line: m.other_line,
94
+ };
95
+ });
96
+
97
+ parentPort.postMessage({ type: 'result', id, messages: clean });
98
+ } catch (e) {
99
+ parentPort.postMessage({ type: 'error', id, error: e.message || String(e) });
100
+ }
101
+ });