bare-script 4.1.2__tar.gz → 4.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {bare_script-4.1.2/src/bare_script.egg-info → bare_script-4.1.3}/PKG-INFO +1 -1
  2. {bare_script-4.1.2 → bare_script-4.1.3}/setup.cfg +1 -1
  3. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/parser.py +179 -156
  4. {bare_script-4.1.2 → bare_script-4.1.3/src/bare_script.egg-info}/PKG-INFO +1 -1
  5. {bare_script-4.1.2 → bare_script-4.1.3}/LICENSE +0 -0
  6. {bare_script-4.1.2 → bare_script-4.1.3}/README.md +0 -0
  7. {bare_script-4.1.2 → bare_script-4.1.3}/pyproject.toml +0 -0
  8. {bare_script-4.1.2 → bare_script-4.1.3}/setup.py +0 -0
  9. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/__init__.py +0 -0
  10. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/__main__.py +0 -0
  11. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/bare.py +0 -0
  12. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/__init__.py +0 -0
  13. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/args.bare +0 -0
  14. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/baredoc.bare +0 -0
  15. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/baredocCLI.bare +0 -0
  16. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/data.bare +0 -0
  17. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/dataLineChart.bare +0 -0
  18. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/dataTable.bare +0 -0
  19. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/dataUtil.bare +0 -0
  20. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/diff.bare +0 -0
  21. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/draw.bare +0 -0
  22. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/elementModel.bare +0 -0
  23. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/forms.bare +0 -0
  24. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/markdown.bare +0 -0
  25. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/markdownElements.bare +0 -0
  26. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/markdownHighlight.bare +0 -0
  27. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/markdownParser.bare +0 -0
  28. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/markdownUp.bare +0 -0
  29. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/pager.bare +0 -0
  30. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/qrcode.bare +0 -0
  31. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/schemaDoc.bare +0 -0
  32. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/unittest.bare +0 -0
  33. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/include/unittestMock.bare +0 -0
  34. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/library.py +0 -0
  35. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/model.py +0 -0
  36. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/options.py +0 -0
  37. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/runtime.py +0 -0
  38. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/runtime_c.c +0 -0
  39. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script/value.py +0 -0
  40. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script.egg-info/SOURCES.txt +0 -0
  41. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script.egg-info/dependency_links.txt +0 -0
  42. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script.egg-info/entry_points.txt +0 -0
  43. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script.egg-info/requires.txt +0 -0
  44. {bare_script-4.1.2 → bare_script-4.1.3}/src/bare_script.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bare-script
3
- Version: 4.1.2
3
+ Version: 4.1.3
4
4
  Summary: bare-script
5
5
  Home-page: https://github.com/craigahobbs/bare-script
6
6
  Author: Craig A. Hobbs
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = bare-script
3
- version = 4.1.2
3
+ version = 4.1.3
4
4
  url = https://github.com/craigahobbs/bare-script
5
5
  author = Craig A. Hobbs
6
6
  author_email = craigahobbs@gmail.com
@@ -44,8 +44,9 @@ def parse_script(script_text, start_line_number=1, script_name=None):
44
44
  for ix_line_part, line_part in enumerate(lines):
45
45
  statements = function_def['function']['statements'] if function_def is not None else script['statements']
46
46
 
47
- # Comment?
48
- if _R_SCRIPT_COMMENT.match(line_part) is not None:
47
+ # Comment or empty line?
48
+ line_part_trimmed = line_part.lstrip()
49
+ if not line_part_trimmed or line_part_trimmed[0] == '#':
49
50
  continue
50
51
 
51
52
  # Set the line index
@@ -74,29 +75,12 @@ def parse_script(script_text, start_line_number=1, script_name=None):
74
75
  if ix_line != ix_line_part:
75
76
  statement_base['lineCount'] = (ix_line_part - ix_line) + 1
76
77
 
77
- # Assignment?
78
- match_assignment = _R_SCRIPT_ASSIGNMENT.match(line)
79
- if match_assignment:
80
- # Parse the expression
81
- try:
82
- assignment_expr = parse_expression(match_assignment.group('expr'), line_number, script_name, True)
83
- except BareScriptParserError as error:
84
- column_number = len(line) - len(match_assignment.group('expr')) + error.column_number
85
- raise BareScriptParserError(error.error, line, column_number, start_line_number + ix_line, script_name)
86
-
87
- # Add the expression statement
88
- expr_statement = {
89
- 'expr': {
90
- 'name': match_assignment.group('name'),
91
- 'expr': assignment_expr,
92
- **statement_base
93
- }
94
- }
95
- statements.append(expr_statement)
96
- continue
78
+ # Determine the line's leading keyword for dispatch
79
+ match_keyword = _R_SCRIPT_KEYWORD.match(line)
80
+ keyword = match_keyword.group(1) if match_keyword else None
97
81
 
98
82
  # Function definition begin?
99
- match_function_begin = _R_SCRIPT_FUNCTION_BEGIN.match(line)
83
+ match_function_begin = _R_SCRIPT_FUNCTION_BEGIN.match(line) if keyword in ('function', 'async') else None
100
84
  if match_function_begin:
101
85
  # Nested function definitions are not allowed
102
86
  if function_def is not None:
@@ -121,7 +105,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
121
105
  continue
122
106
 
123
107
  # Function definition end?
124
- match_function_end = _R_SCRIPT_FUNCTION_END.match(line)
108
+ match_function_end = _R_SCRIPT_FUNCTION_END.match(line) if keyword == 'endfunction' else None
125
109
  if match_function_end:
126
110
  if function_def is None:
127
111
  raise BareScriptParserError('No matching function definition', line, 1, start_line_number + ix_line, script_name)
@@ -138,7 +122,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
138
122
  continue
139
123
 
140
124
  # If-then begin?
141
- match_if_begin = _R_SCRIPT_IF_BEGIN.match(line)
125
+ match_if_begin = _R_SCRIPT_IF_BEGIN.match(line) if keyword == 'if' else None
142
126
  if match_if_begin:
143
127
  # Parse the if-then expression
144
128
  try:
@@ -167,7 +151,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
167
151
  continue
168
152
 
169
153
  # Else-if-then?
170
- match_if_else_if = _R_SCRIPT_IF_ELSE_IF.match(line)
154
+ match_if_else_if = _R_SCRIPT_IF_ELSE_IF.match(line) if keyword == 'elif' else None
171
155
  if match_if_else_if:
172
156
  # Get the else-if-then definition
173
157
  label_def_depth = function_label_def_depth if function_def is not None else 0
@@ -177,7 +161,9 @@ def parse_script(script_text, start_line_number=1, script_name=None):
177
161
 
178
162
  # Cannot come after the else-then statement
179
163
  if ifthen['hasElse']:
180
- raise BareScriptParserError('Elif statement following else statement', line, 1, start_line_number + ix_line, script_name)
164
+ raise BareScriptParserError(
165
+ 'Elif statement following else statement', line, 1, start_line_number + ix_line, script_name
166
+ )
181
167
 
182
168
  # Parse teh else-if-then expression
183
169
  try:
@@ -204,7 +190,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
204
190
  continue
205
191
 
206
192
  # Else-then?
207
- match_if_else = _R_SCRIPT_IF_ELSE.match(line)
193
+ match_if_else = _R_SCRIPT_IF_ELSE.match(line) if keyword == 'else' else None
208
194
  if match_if_else:
209
195
  # Get the if-then definition
210
196
  label_def_depth = function_label_def_depth if function_def is not None else 0
@@ -225,7 +211,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
225
211
  continue
226
212
 
227
213
  # If-then end?
228
- match_if_end = _R_SCRIPT_IF_END.match(line)
214
+ match_if_end = _R_SCRIPT_IF_END.match(line) if keyword == 'endif' else None
229
215
  if match_if_end:
230
216
  # Pop the if-then definition
231
217
  label_def_depth = function_label_def_depth if function_def is not None else 0
@@ -242,7 +228,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
242
228
  continue
243
229
 
244
230
  # While-do begin?
245
- match_while_begin = _R_SCRIPT_WHILE_BEGIN.match(line)
231
+ match_while_begin = _R_SCRIPT_WHILE_BEGIN.match(line) if keyword == 'while' else None
246
232
  if match_while_begin:
247
233
  # Parse the while-do expression
248
234
  try:
@@ -271,7 +257,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
271
257
  continue
272
258
 
273
259
  # While-do end?
274
- match_while_end = _R_SCRIPT_WHILE_END.match(line)
260
+ match_while_end = _R_SCRIPT_WHILE_END.match(line) if keyword == 'endwhile' else None
275
261
  if match_while_end:
276
262
  # Pop the while-do definition
277
263
  label_def_depth = function_label_def_depth if function_def is not None else 0
@@ -290,7 +276,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
290
276
  continue
291
277
 
292
278
  # For-each begin?
293
- match_for_begin = _R_SCRIPT_FOR_BEGIN.match(line)
279
+ match_for_begin = _R_SCRIPT_FOR_BEGIN.match(line) if keyword == 'for' else None
294
280
  if match_for_begin:
295
281
  # Add the for-each label
296
282
  foreach = {
@@ -335,14 +321,19 @@ def parse_script(script_text, start_line_number=1, script_name=None):
335
321
  {'label': {'name': foreach['loop'], **statement_base}},
336
322
  {'expr': {
337
323
  'name': foreach['value'],
338
- 'expr': {'function': {'name': 'arrayGet', 'args': [{'variable': foreach['values']}, {'variable': foreach['index']}]}},
324
+ 'expr': {
325
+ 'function': {
326
+ 'name': 'arrayGet',
327
+ 'args': [{'variable': foreach['values']}, {'variable': foreach['index']}]
328
+ }
329
+ },
339
330
  **statement_base
340
331
  }}
341
332
  ])
342
333
  continue
343
334
 
344
335
  # For-each end?
345
- match_for_end = _R_SCRIPT_FOR_END.match(line)
336
+ match_for_end = _R_SCRIPT_FOR_END.match(line) if keyword == 'endfor' else None
346
337
  if match_for_end:
347
338
  # Pop the foreach definition
348
339
  label_def_depth = function_label_def_depth if function_def is not None else 0
@@ -364,7 +355,9 @@ def parse_script(script_text, start_line_number=1, script_name=None):
364
355
  }},
365
356
  {'jump': {
366
357
  'label': foreach['loop'],
367
- 'expr': {'binary': {'op': '<', 'left': {'variable': foreach['index']}, 'right': {'variable': foreach['length']}}},
358
+ 'expr': {
359
+ 'binary': {'op': '<', 'left': {'variable': foreach['index']}, 'right': {'variable': foreach['length']}}
360
+ },
368
361
  **statement_base
369
362
  }},
370
363
  {'label': {'name': foreach['done'], **statement_base}}
@@ -372,7 +365,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
372
365
  continue
373
366
 
374
367
  # Break statement?
375
- match_break = _R_SCRIPT_BREAK.match(line)
368
+ match_break = _R_SCRIPT_BREAK.match(line) if keyword == 'break' else None
376
369
  if match_break:
377
370
  # Get the loop definition
378
371
  label_def_depth = function_label_def_depth if function_def is not None else 0
@@ -388,13 +381,15 @@ def parse_script(script_text, start_line_number=1, script_name=None):
388
381
  continue
389
382
 
390
383
  # Continue statement?
391
- match_continue = _R_SCRIPT_CONTINUE.match(line)
384
+ match_continue = _R_SCRIPT_CONTINUE.match(line) if keyword == 'continue' else None
392
385
  if match_continue:
393
386
  # Get the loop definition
394
387
  label_def_depth = function_label_def_depth if function_def is not None else 0
395
388
  ix_label_def = next((i for i, d in reversed(list(enumerate(label_defs))) if 'if' not in d), -1)
396
389
  if ix_label_def < label_def_depth:
397
- raise BareScriptParserError('Continue statement outside of loop', line, 1, start_line_number + ix_line, script_name)
390
+ raise BareScriptParserError(
391
+ 'Continue statement outside of loop', line, 1, start_line_number + ix_line, script_name
392
+ )
398
393
  label_def = label_defs[ix_label_def]
399
394
  label_key = next(iter(label_def))
400
395
  loop_def = label_def[label_key]
@@ -404,14 +399,8 @@ def parse_script(script_text, start_line_number=1, script_name=None):
404
399
  statements.append({'jump': {'label': loop_def['continue'], **statement_base}})
405
400
  continue
406
401
 
407
- # Label definition?
408
- match_label = _R_SCRIPT_LABEL.match(line)
409
- if match_label:
410
- statements.append({'label': {'name': match_label.group('name'), **statement_base}})
411
- continue
412
-
413
402
  # Jump definition?
414
- match_jump = _R_SCRIPT_JUMP.match(line)
403
+ match_jump = _R_SCRIPT_JUMP.match(line) if keyword in ('jump', 'jumpif') else None
415
404
  if match_jump:
416
405
  jump_statement = {'jump': {'label': match_jump.group('name'), **statement_base}}
417
406
  if match_jump.group('expr'):
@@ -424,12 +413,14 @@ def parse_script(script_text, start_line_number=1, script_name=None):
424
413
  continue
425
414
 
426
415
  # Return definition?
427
- match_return = _R_SCRIPT_RETURN.match(line)
416
+ match_return = _R_SCRIPT_RETURN.match(line) if keyword == 'return' else None
428
417
  if match_return:
429
418
  return_statement = {'return': {**statement_base}}
430
419
  if match_return.group('expr'):
431
420
  try:
432
- return_statement['return']['expr'] = parse_expression(match_return.group('expr'), line_number, script_name, True)
421
+ return_statement['return']['expr'] = parse_expression(
422
+ match_return.group('expr'), line_number, script_name, True
423
+ )
433
424
  except BareScriptParserError as error:
434
425
  column_number = len(match_return.group('return')) - len(match_return.group('expr')) + error.column_number
435
426
  raise BareScriptParserError(error.error, line, column_number, start_line_number + ix_line, script_name)
@@ -437,7 +428,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
437
428
  continue
438
429
 
439
430
  # Include definition?
440
- match_include = _R_SCRIPT_INCLUDE.match(line) or _R_SCRIPT_INCLUDE_SYSTEM.match(line)
431
+ match_include = _R_SCRIPT_INCLUDE.match(line) or _R_SCRIPT_INCLUDE_SYSTEM.match(line) if keyword == 'include' else None
441
432
  if match_include:
442
433
  delim = match_include.group('delim')
443
434
  url = match_include.group('url') if delim == '<' else \
@@ -451,6 +442,35 @@ def parse_script(script_text, start_line_number=1, script_name=None):
451
442
  include_statement['include']['includes'].append({'url': url, 'system': True} if delim == '<' else {'url': url})
452
443
  continue
453
444
 
445
+ # Catch-all (line starts with an identifier but isn't a recognized control statement)
446
+ if keyword is not None:
447
+ # Assignment?
448
+ match_assignment = _R_SCRIPT_ASSIGNMENT.match(line)
449
+ if match_assignment:
450
+ # Parse the expression
451
+ try:
452
+ assignment_expr = parse_expression(match_assignment.group('expr'), line_number, script_name, True)
453
+ except BareScriptParserError as error:
454
+ column_number = len(line) - len(match_assignment.group('expr')) + error.column_number
455
+ raise BareScriptParserError(error.error, line, column_number, start_line_number + ix_line, script_name)
456
+
457
+ # Add the expression statement
458
+ expr_statement = {
459
+ 'expr': {
460
+ 'name': match_assignment.group('name'),
461
+ 'expr': assignment_expr,
462
+ **statement_base
463
+ }
464
+ }
465
+ statements.append(expr_statement)
466
+ continue
467
+
468
+ # Label definition?
469
+ match_label = _R_SCRIPT_LABEL.match(line)
470
+ if match_label:
471
+ statements.append({'label': {'name': match_label.group('name'), **statement_base}})
472
+ continue
473
+
454
474
  # Expression
455
475
  try:
456
476
  expr_statement = {'expr': {'expr': parse_expression(line, line_number, script_name, True), **statement_base}}
@@ -471,7 +491,7 @@ def parse_script(script_text, start_line_number=1, script_name=None):
471
491
  # BareScript regex
472
492
  _R_SCRIPT_LINE_SPLIT = re.compile(r'\r?\n')
473
493
  _R_SCRIPT_CONTINUATION = re.compile(r'\\\s*$')
474
- _R_SCRIPT_COMMENT = re.compile(r'^\s*(?:#.*)?$')
494
+ _R_SCRIPT_KEYWORD = re.compile(r'^\s*([A-Za-z_]\w*)')
475
495
  _R_SCRIPT_ASSIGNMENT = re.compile(r'^\s*(?P<name>[A-Za-z_]\w*)\s*=\s*(?P<expr>.+)$')
476
496
  _R_PART_COMMENT = r'\s*(#.*)?$'
477
497
  _R_SCRIPT_FUNCTION_BEGIN = re.compile(
@@ -516,7 +536,7 @@ def parse_expression(expr_text, line_number=None, script_name=None, array_litera
516
536
  :raises BareScriptParserError: A parsing error occurred
517
537
  """
518
538
  try:
519
- expr, next_text = _parse_binary_expression(expr_text, None, array_literals)
539
+ expr, next_text = _parse_binary_expression(expr_text, array_literals)
520
540
  if next_text.strip() != '':
521
541
  raise BareScriptParserError('Syntax error', next_text, 1, line_number, script_name)
522
542
  return expr
@@ -526,42 +546,47 @@ def parse_expression(expr_text, line_number=None, script_name=None, array_litera
526
546
 
527
547
 
528
548
  # Helper function to parse a binary operator expression chain
529
- def _parse_binary_expression(expr_text, bin_left_expr, array_literals):
530
- # Parse the binary operator's left unary expression if none was passed
531
- if bin_left_expr is not None:
532
- bin_text = expr_text
533
- left_expr = bin_left_expr
534
- else:
535
- left_expr, bin_text = _parse_unary_expression(expr_text, array_literals)
536
-
537
- # Match a binary operator - if not found, return the left expression
538
- match_binary_op = _R_EXPR_BINARY_OP.match(bin_text)
539
- if match_binary_op is None:
540
- # End-of-line comment?
541
- if _R_EXPR_COMMENT.match(bin_text):
542
- bin_text = ''
543
-
544
- return [left_expr, bin_text]
545
- bin_op = match_binary_op.group(1)
546
- right_text = bin_text[len(match_binary_op.group(0)):]
547
-
548
- # Parse the right sub-expression
549
- right_expr, next_text = _parse_unary_expression(right_text, array_literals)
550
-
551
- # Create the binary expression - re-order for binary operators as necessary
552
- bin_expr = None
553
- if 'binary' in left_expr and bin_op in BINARY_REORDER and left_expr['binary']['op'] in BINARY_REORDER[bin_op]:
554
- # Left expression has lower precedence - find where to put this expression within the left expression
555
- bin_expr = left_expr
556
- reorder_expr = left_expr
557
- while 'binary' in reorder_expr['binary']['right'] and reorder_expr['binary']['right']['binary']['op'] in BINARY_REORDER[bin_op]:
558
- reorder_expr = reorder_expr['binary']['right']
559
- reorder_expr['binary']['right'] = {'binary': {'op': bin_op, 'left': reorder_expr['binary']['right'], 'right': right_expr}}
560
- else:
561
- bin_expr = {'binary': {'op': bin_op, 'left': left_expr, 'right': right_expr}}
549
+ def _parse_binary_expression(expr_text, array_literals):
550
+ # Parse the binary operator's left unary expression
551
+ left_expr, bin_text = _parse_unary_expression(expr_text, array_literals)
552
+
553
+ # Consume binary operators while present, building up the binary expression tree
554
+ while True:
555
+ # Skip leading whitespace to find the first dispatch char
556
+ pos = 0
557
+ n = len(bin_text)
558
+ while pos < n and bin_text[pos] in ' \t\r\n\v\f':
559
+ pos += 1
560
+
561
+ # End of binary expression (empty, whitespace-only, or end-of-line comment)?
562
+ if pos >= n or bin_text[pos] == '#':
563
+ return [left_expr, '']
564
+
565
+ # Binary operator? First char must be one of "!%&*+-/<=>^|"
566
+ is_binary_op_start = bin_text[pos] in '!%&*+-/<=>^|'
567
+ match_binary_op = _R_EXPR_BINARY_OP.match(bin_text) if is_binary_op_start else None
568
+ if match_binary_op is None:
569
+ return [left_expr, bin_text]
570
+ bin_op = match_binary_op.group(1)
571
+ right_text = bin_text[len(match_binary_op.group(0)):]
572
+
573
+ # Parse the right sub-expression
574
+ right_expr, next_text = _parse_unary_expression(right_text, array_literals)
575
+
576
+ # Create the binary expression - re-order for binary operators as necessary
577
+ if 'binary' in left_expr and bin_op in BINARY_REORDER and left_expr['binary']['op'] in BINARY_REORDER[bin_op]:
578
+ # Left expression has lower precedence - find where to put this expression within the left expression
579
+ reorder_expr = left_expr
580
+ while ('binary' in reorder_expr['binary']['right']
581
+ and reorder_expr['binary']['right']['binary']['op'] in BINARY_REORDER[bin_op]):
582
+ reorder_expr = reorder_expr['binary']['right']
583
+ reorder_expr['binary']['right'] = {
584
+ 'binary': {'op': bin_op, 'left': reorder_expr['binary']['right'], 'right': right_expr}
585
+ }
586
+ else:
587
+ left_expr = {'binary': {'op': bin_op, 'left': left_expr, 'right': right_expr}}
562
588
 
563
- # Parse the next binary expression in the chain
564
- return _parse_binary_expression(next_text, bin_expr, array_literals)
589
+ bin_text = next_text
565
590
 
566
591
 
567
592
  # Binary operator re-order map
@@ -590,48 +615,54 @@ BINARY_REORDER = {
590
615
 
591
616
  # Helper function to parse a unary expression
592
617
  def _parse_unary_expression(expr_text, array_literals):
593
- # Group open?
594
- match_group_open = _R_EXPR_GROUP_OPEN.match(expr_text)
618
+ # Skip leading whitespace to find the first dispatch char
619
+ pos = 0
620
+ n = len(expr_text)
621
+ while pos < n and expr_text[pos] in ' \t\r\n\v\f':
622
+ pos += 1
623
+ if pos >= n:
624
+ raise BareScriptParserError('Syntax error', expr_text, 1, None, None)
625
+ c = expr_text[pos]
626
+
627
+ # Group? '('
628
+ match_group_open = _R_EXPR_GROUP_OPEN.match(expr_text) if c == '(' else None
595
629
  if match_group_open:
596
630
  group_text = expr_text[len(match_group_open.group(0)):]
597
- expr, next_text = _parse_binary_expression(group_text, None, array_literals)
631
+ expr, next_text = _parse_binary_expression(group_text, array_literals)
598
632
  match_group_close = _R_EXPR_GROUP_CLOSE.match(next_text)
599
633
  if match_group_close is None:
600
634
  raise BareScriptParserError('Unmatched parenthesis', expr_text, 1, None, None)
601
635
  return [{'group': expr}, next_text[len(match_group_close.group(0)):]]
602
636
 
603
- # Number?
604
- match_number = _R_EXPR_NUMBER.match(expr_text)
637
+ # Number? digit, '+', or '-' (the '-' falls through to unary below if no number matches)
638
+ match_number = _R_EXPR_NUMBER.match(expr_text) if c.isdigit() or c in '+-' else None
605
639
  if match_number:
606
640
  number_str = match_number.group(1)
607
641
  number = float(int(number_str, base=16)) if number_str.startswith('0x') else float(number_str)
608
- expr = {'number': number}
609
- return [expr, expr_text[len(match_number.group(0)):]]
642
+ return [{'number': number}, expr_text[len(match_number.group(0)):]]
610
643
 
611
- # String?
612
- match_string = _R_EXPR_STRING.match(expr_text)
644
+ # String? '\''
645
+ match_string = _R_EXPR_STRING.match(expr_text) if c == "'" else None
613
646
  if match_string:
614
647
  string = _R_EXPR_STRING_ESCAPES.sub(_replace_string_escape, match_string.group(1))
615
- expr = {'string': string}
616
- return [expr, expr_text[len(match_string.group(0)):]]
648
+ return [{'string': string}, expr_text[len(match_string.group(0)):]]
617
649
 
618
- # String (double quotes)?
619
- match_string_double = _R_EXPR_STRING_DOUBLE.match(expr_text)
650
+ # String (double quotes)? '"'
651
+ match_string_double = _R_EXPR_STRING_DOUBLE.match(expr_text) if c == '"' else None
620
652
  if match_string_double:
621
653
  string = _R_EXPR_STRING_ESCAPES.sub(_replace_string_escape, match_string_double.group(1))
622
- expr = {'string': string}
623
- return [expr, expr_text[len(match_string_double.group(0)):]]
654
+ return [{'string': string}, expr_text[len(match_string_double.group(0)):]]
624
655
 
625
- # Unary operator?
626
- match_unary = _R_EXPR_UNARY_OP.match(expr_text)
656
+ # Unary operator? '!', '-', '~'
657
+ match_unary = _R_EXPR_UNARY_OP.match(expr_text) if c in '!-~' else None
627
658
  if match_unary:
628
659
  unary_text = expr_text[len(match_unary.group(0)):]
629
660
  expr, next_text = _parse_unary_expression(unary_text, array_literals)
630
- unary_expr = {'unary': {'op': match_unary.group(1), 'expr': expr}}
631
- return [unary_expr, next_text]
661
+ return [{'unary': {'op': match_unary.group(1), 'expr': expr}}, next_text]
632
662
 
633
- # Function?
634
- match_function_open = _R_EXPR_FUNCTION_OPEN.match(expr_text)
663
+ # Function call? identifier followed by '('
664
+ is_ident = c.isalpha() or c == '_'
665
+ match_function_open = _R_EXPR_FUNCTION_OPEN.match(expr_text) if is_ident else None
635
666
  if match_function_open:
636
667
  arg_text = expr_text[len(match_function_open.group(0)):]
637
668
  args = []
@@ -650,15 +681,19 @@ def _parse_unary_expression(expr_text, array_literals):
650
681
  arg_text = arg_text[len(match_function_separator.group(0)):]
651
682
 
652
683
  # Get the argument
653
- arg_expr, next_arg_text = _parse_binary_expression(arg_text, None, array_literals)
684
+ arg_expr, next_arg_text = _parse_binary_expression(arg_text, array_literals)
654
685
  args.append(arg_expr)
655
686
  arg_text = next_arg_text
656
687
 
657
- fn_expr = {'function': {'name': match_function_open.group(1), 'args': args}}
658
- return [fn_expr, arg_text]
688
+ return [{'function': {'name': match_function_open.group(1), 'args': args}}, arg_text]
659
689
 
660
- # Object creation?
661
- match_object_open = _R_EXPR_OBJECT_OPEN.match(expr_text)
690
+ # Variable
691
+ match_variable = _R_EXPR_VARIABLE.match(expr_text) if is_ident else None
692
+ if match_variable:
693
+ return [{'variable': match_variable.group(1)}, expr_text[len(match_variable.group(0)):]]
694
+
695
+ # Object creation? '{'
696
+ match_object_open = _R_EXPR_OBJECT_OPEN.match(expr_text) if c == '{' else None
662
697
  if match_object_open:
663
698
  arg_text = expr_text[len(match_object_open.group(0)):]
664
699
  args = []
@@ -677,7 +712,7 @@ def _parse_unary_expression(expr_text, array_literals):
677
712
  arg_text = arg_text[len(match_object_separator.group(0)):]
678
713
 
679
714
  # Get the key
680
- arg_key, arg_text = _parse_binary_expression(arg_text, None, array_literals)
715
+ arg_key, arg_text = _parse_binary_expression(arg_text, array_literals)
681
716
  args.append(arg_key)
682
717
 
683
718
  # Key/value separator
@@ -687,58 +722,46 @@ def _parse_unary_expression(expr_text, array_literals):
687
722
  arg_text = arg_text[len(match_object_separator_key.group(0)):]
688
723
 
689
724
  # Get the value
690
- arg_value, arg_text = _parse_binary_expression(arg_text, None, array_literals)
725
+ arg_value, arg_text = _parse_binary_expression(arg_text, array_literals)
691
726
  args.append(arg_value)
692
727
 
693
- fn_expr = {'function': {'name': 'objectNew', 'args': args}}
694
- return [fn_expr, arg_text]
695
-
696
- # Array creation?
697
- if array_literals:
698
- match_array_open = _R_EXPR_ARRAY_OPEN.match(expr_text)
699
- if match_array_open:
700
- arg_text = expr_text[len(match_array_open.group(0)):]
701
- args = []
702
- while True:
703
- # Array close?
704
- match_array_close = _R_EXPR_ARRAY_CLOSE.match(arg_text)
705
- if match_array_close:
706
- arg_text = arg_text[len(match_array_close.group(0)):]
707
- break
708
-
709
- # Array key separator
710
- if args:
711
- match_array_separator = _R_EXPR_ARRAY_SEPARATOR.match(arg_text)
712
- if match_array_separator is None:
713
- raise BareScriptParserError('Syntax error', arg_text, 1, None, None)
714
- arg_text = arg_text[len(match_array_separator.group(0)):]
715
-
716
- # Get the value
717
- arg_value, arg_text = _parse_binary_expression(arg_text, None, array_literals)
718
- args.append(arg_value)
719
-
720
- fn_expr = {'function': {'name': 'arrayNew', 'args': args}}
721
- return [fn_expr, arg_text]
722
-
723
- # Variable?
724
- match_variable = _R_EXPR_VARIABLE.match(expr_text)
725
- if match_variable:
726
- expr = {'variable': match_variable.group(1)}
727
- return [expr, expr_text[len(match_variable.group(0)):]]
728
+ return [{'function': {'name': 'objectNew', 'args': args}}, arg_text]
729
+
730
+ # Array creation? '['
731
+ match_array_open = _R_EXPR_ARRAY_OPEN.match(expr_text) if c == '[' and array_literals else None
732
+ if match_array_open:
733
+ arg_text = expr_text[len(match_array_open.group(0)):]
734
+ args = []
735
+ while True:
736
+ # Array close?
737
+ match_array_close = _R_EXPR_ARRAY_CLOSE.match(arg_text)
738
+ if match_array_close:
739
+ arg_text = arg_text[len(match_array_close.group(0)):]
740
+ break
741
+
742
+ # Array key separator
743
+ if args:
744
+ match_array_separator = _R_EXPR_ARRAY_SEPARATOR.match(arg_text)
745
+ if match_array_separator is None:
746
+ raise BareScriptParserError('Syntax error', arg_text, 1, None, None)
747
+ arg_text = arg_text[len(match_array_separator.group(0)):]
748
+
749
+ # Get the value
750
+ arg_value, arg_text = _parse_binary_expression(arg_text, array_literals)
751
+ args.append(arg_value)
752
+
753
+ return [{'function': {'name': 'arrayNew', 'args': args}}, arg_text]
728
754
 
729
- # Variable (brackets)?
730
- if not array_literals:
731
- match_variable_ex = _R_EXPR_VARIABLE_EX.match(expr_text)
732
- if match_variable_ex:
733
- variable_name = _R_EXPR_VARIABLE_EX_ESCAPE.sub('\\1', match_variable_ex.group(1))
734
- expr = {'variable': variable_name}
735
- return [expr, expr_text[len(match_variable_ex.group(0)):]]
755
+ # Variable (brackets)? '['
756
+ match_variable_ex = _R_EXPR_VARIABLE_EX.match(expr_text) if c == '[' and not array_literals else None
757
+ if match_variable_ex:
758
+ variable_name = _R_EXPR_VARIABLE_EX_ESCAPE.sub('\\1', match_variable_ex.group(1))
759
+ return [{'variable': variable_name}, expr_text[len(match_variable_ex.group(0)):]]
736
760
 
737
761
  raise BareScriptParserError('Syntax error', expr_text, 1, None, None)
738
762
 
739
763
 
740
764
  # BareScript expression regex
741
- _R_EXPR_COMMENT = re.compile(r'^\s*#.*$')
742
765
  _R_EXPR_BINARY_OP = re.compile(r'^\s*(\*\*|\*|\/|%|\+|-|<<|>>|<=|<|>=|>|==|!=|&&|\|\||&|\^|\|)')
743
766
  _R_EXPR_UNARY_OP = re.compile(r'^\s*(!|-|~)')
744
767
  _R_EXPR_FUNCTION_OPEN = re.compile(r'^\s*([A-Za-z_]\w+)\s*\(')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bare-script
3
- Version: 4.1.2
3
+ Version: 4.1.3
4
4
  Summary: bare-script
5
5
  Home-page: https://github.com/craigahobbs/bare-script
6
6
  Author: Craig A. Hobbs
File without changes
File without changes
File without changes
File without changes