execsql2 2.1.2__py3-none-any.whl → 2.4.0__py3-none-any.whl

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 (94) hide show
  1. execsql/cli/__init__.py +436 -0
  2. execsql/cli/dsn.py +86 -0
  3. execsql/cli/help.py +140 -0
  4. execsql/{cli.py → cli/run.py} +14 -589
  5. execsql/config.py +65 -1
  6. execsql/db/access.py +27 -15
  7. execsql/db/base.py +328 -215
  8. execsql/db/dsn.py +10 -5
  9. execsql/db/duckdb.py +6 -2
  10. execsql/db/factory.py +21 -0
  11. execsql/db/firebird.py +27 -19
  12. execsql/db/mysql.py +12 -7
  13. execsql/db/oracle.py +15 -11
  14. execsql/db/postgres.py +31 -16
  15. execsql/db/sqlite.py +15 -11
  16. execsql/db/sqlserver.py +16 -5
  17. execsql/exceptions.py +25 -7
  18. execsql/exporters/base.py +12 -1
  19. execsql/exporters/delimited.py +80 -35
  20. execsql/exporters/duckdb.py +6 -2
  21. execsql/exporters/feather.py +10 -6
  22. execsql/exporters/html.py +89 -69
  23. execsql/exporters/json.py +52 -45
  24. execsql/exporters/latex.py +37 -27
  25. execsql/exporters/ods.py +32 -11
  26. execsql/exporters/parquet.py +5 -2
  27. execsql/exporters/pretty.py +16 -9
  28. execsql/exporters/raw.py +22 -16
  29. execsql/exporters/sqlite.py +6 -2
  30. execsql/exporters/templates.py +39 -21
  31. execsql/exporters/values.py +26 -20
  32. execsql/exporters/xls.py +30 -11
  33. execsql/exporters/xml.py +31 -13
  34. execsql/exporters/zip.py +15 -0
  35. execsql/importers/base.py +6 -4
  36. execsql/importers/csv.py +8 -6
  37. execsql/importers/feather.py +6 -4
  38. execsql/importers/ods.py +6 -4
  39. execsql/importers/xls.py +6 -4
  40. execsql/metacommands/__init__.py +208 -1548
  41. execsql/metacommands/conditions.py +101 -27
  42. execsql/metacommands/control.py +8 -4
  43. execsql/metacommands/data.py +6 -6
  44. execsql/metacommands/debug.py +6 -2
  45. execsql/metacommands/dispatch.py +2011 -0
  46. execsql/metacommands/io.py +67 -1310
  47. execsql/metacommands/io_export.py +442 -0
  48. execsql/metacommands/io_fileops.py +287 -0
  49. execsql/metacommands/io_import.py +398 -0
  50. execsql/metacommands/io_write.py +248 -0
  51. execsql/metacommands/prompt.py +22 -66
  52. execsql/metacommands/system.py +7 -2
  53. execsql/models.py +7 -0
  54. execsql/parser.py +10 -0
  55. execsql/py.typed +0 -0
  56. execsql/script/__init__.py +95 -0
  57. execsql/script/control.py +162 -0
  58. execsql/{script.py → script/engine.py} +184 -402
  59. execsql/script/variables.py +281 -0
  60. execsql/types.py +49 -20
  61. execsql/utils/auth.py +2 -0
  62. execsql/utils/crypto.py +4 -6
  63. execsql/utils/datetime.py +1 -0
  64. execsql/utils/errors.py +11 -0
  65. execsql/utils/fileio.py +33 -8
  66. execsql/utils/gui.py +46 -0
  67. execsql/utils/mail.py +7 -17
  68. execsql/utils/numeric.py +2 -0
  69. execsql/utils/regex.py +9 -0
  70. execsql/utils/strings.py +16 -0
  71. execsql/utils/timer.py +2 -0
  72. execsql2-2.4.0.data/data/execsql2_extras/README.md +65 -0
  73. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/execsql.conf +1 -1
  74. {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/METADATA +13 -6
  75. execsql2-2.4.0.dist-info/RECORD +108 -0
  76. execsql2-2.1.2.data/data/execsql2_extras/READ_ME.rst +0 -127
  77. execsql2-2.1.2.dist-info/RECORD +0 -96
  78. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/config_settings.sqlite +0 -0
  79. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
  80. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/make_config_db.sql +0 -0
  81. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/md_compare.sql +0 -0
  82. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/md_glossary.sql +0 -0
  83. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/md_upsert.sql +0 -0
  84. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/pg_compare.sql +0 -0
  85. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/pg_glossary.sql +0 -0
  86. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/pg_upsert.sql +0 -0
  87. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/script_template.sql +0 -0
  88. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/ss_compare.sql +0 -0
  89. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/ss_glossary.sql +0 -0
  90. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/ss_upsert.sql +0 -0
  91. {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/WHEEL +0 -0
  92. {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/entry_points.txt +0 -0
  93. {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/licenses/LICENSE.txt +0 -0
  94. {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,2011 @@
1
+ """Build function for the execsql metacommand dispatch table.
2
+
3
+ This module contains ``build_dispatch_table()``, which constructs and returns
4
+ a :class:`~execsql.script.MetaCommandList` populated with every metacommand
5
+ regex and its handler function.
6
+
7
+ It is imported by ``execsql.metacommands`` where the module-level
8
+ ``DISPATCH_TABLE`` is assigned.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import re
14
+
15
+ # Handler imports — grouped by module for readability.
16
+ from execsql.metacommands.connect import (
17
+ x_autocommit_off,
18
+ x_autocommit_on,
19
+ x_connect_access,
20
+ x_connect_duckdb,
21
+ x_connect_dsn,
22
+ x_connect_fb,
23
+ x_connect_mysql,
24
+ x_connect_ora,
25
+ x_connect_pg,
26
+ x_connect_sqlite,
27
+ x_connect_ssvr,
28
+ x_connect_user_fb,
29
+ x_connect_user_mysql,
30
+ x_connect_user_ora,
31
+ x_connect_user_pg,
32
+ x_connect_user_ssvr,
33
+ x_daoflushdelay,
34
+ x_disconnect,
35
+ x_pg_vacuum,
36
+ x_use,
37
+ )
38
+ from execsql.metacommands.control import (
39
+ x_begin_batch,
40
+ x_break,
41
+ x_end_batch,
42
+ x_error_halt,
43
+ x_halt,
44
+ x_halt_msg,
45
+ x_if,
46
+ x_if_andif,
47
+ x_if_block,
48
+ x_if_else,
49
+ x_if_elseif,
50
+ x_if_end,
51
+ x_if_orif,
52
+ x_loop,
53
+ x_metacommand_error_halt,
54
+ x_rollback,
55
+ x_wait_until,
56
+ )
57
+ from execsql.metacommands.data import (
58
+ x_boolean_int,
59
+ x_boolean_words,
60
+ x_clean_col_hdrs,
61
+ x_create_col_hdrs,
62
+ x_del_empty_cols,
63
+ x_dedup_col_hdrs,
64
+ x_empty_rows,
65
+ x_empty_strings,
66
+ x_fold_col_hdrs,
67
+ x_import_common_cols_only,
68
+ x_max_int,
69
+ x_only_strings,
70
+ x_quote_all_text,
71
+ x_replace_newlines,
72
+ x_reset_counter,
73
+ x_reset_counters,
74
+ x_rm_sub,
75
+ x_selectsub,
76
+ x_set_counter,
77
+ x_sub,
78
+ x_sub_add,
79
+ x_sub_append,
80
+ x_sub_decrypt,
81
+ x_sub_empty,
82
+ x_sub_encrypt,
83
+ x_sub_ini,
84
+ x_sub_local,
85
+ x_sub_querystring,
86
+ x_sub_tempfile,
87
+ x_subdata,
88
+ x_trim_col_hdrs,
89
+ x_trim_strings,
90
+ x_prompt_selectsub,
91
+ )
92
+ from execsql.metacommands.debug import (
93
+ x_debug_commandliststack,
94
+ x_debug_iflevels,
95
+ x_debug_log_config,
96
+ x_debug_log_subvars,
97
+ x_debug_write_config,
98
+ x_debug_write_metacommands,
99
+ x_debug_write_odbc_drivers,
100
+ x_debug_write_subvars,
101
+ )
102
+ from execsql.metacommands.io import (
103
+ x_cd,
104
+ x_copy,
105
+ x_copy_query,
106
+ x_export,
107
+ x_export_metadata,
108
+ x_export_metadata_table,
109
+ x_export_ods_multiple,
110
+ x_export_query,
111
+ x_export_query_with_template,
112
+ x_export_row_buffer,
113
+ x_export_with_template,
114
+ x_hdf5_text_len,
115
+ x_import,
116
+ x_import_feather,
117
+ x_import_file,
118
+ x_import_ods,
119
+ x_import_ods_pattern,
120
+ x_import_parquet,
121
+ x_import_row_buffer,
122
+ x_import_xls,
123
+ x_import_xls_pattern,
124
+ x_include,
125
+ x_make_export_dirs,
126
+ x_rm_file,
127
+ x_scan_lines,
128
+ x_serve,
129
+ x_show_progress,
130
+ x_write,
131
+ x_write_create_table,
132
+ x_write_create_table_alias,
133
+ x_write_create_table_ods,
134
+ x_write_create_table_xls,
135
+ x_write_prefix,
136
+ x_write_suffix,
137
+ x_writescript,
138
+ x_zip,
139
+ x_zip_buffer_mb,
140
+ )
141
+ from execsql.metacommands.prompt import (
142
+ x_ask,
143
+ x_msg,
144
+ x_pause,
145
+ x_prompt,
146
+ x_prompt_action,
147
+ x_prompt_ask,
148
+ x_prompt_ask_compare,
149
+ x_prompt_compare,
150
+ x_prompt_connect,
151
+ x_prompt_credentials,
152
+ x_prompt_directory,
153
+ x_prompt_enter,
154
+ x_prompt_entryform,
155
+ x_prompt_map,
156
+ x_prompt_openfile,
157
+ x_prompt_pause,
158
+ x_prompt_savefile,
159
+ x_prompt_select_rows,
160
+ x_reset_dialog_canceled,
161
+ )
162
+ from execsql.metacommands.script_ext import (
163
+ x_executescript,
164
+ x_extendscript,
165
+ x_extendscript_metacommand,
166
+ x_extendscript_sql,
167
+ )
168
+ from execsql.metacommands.system import (
169
+ x_cancel_halt,
170
+ x_cancel_halt_email,
171
+ x_cancel_halt_email_clear,
172
+ x_cancel_halt_exec_clear,
173
+ x_cancel_halt_write,
174
+ x_cancel_halt_write_clear,
175
+ x_console,
176
+ x_console_hideshow,
177
+ x_consoleprogress,
178
+ x_consolesave,
179
+ x_consolestatus,
180
+ x_consolewait,
181
+ x_consolewait_onerror,
182
+ x_consolewait_whendone,
183
+ x_consoleheight,
184
+ x_consolewidth,
185
+ x_email,
186
+ x_error_halt_email,
187
+ x_error_halt_email_clear,
188
+ x_error_halt_exec_clear,
189
+ x_error_halt_write,
190
+ x_error_halt_write_clear,
191
+ x_execute,
192
+ x_gui_level,
193
+ x_log,
194
+ x_log_datavars,
195
+ x_log_sql,
196
+ x_logwritemessages,
197
+ x_system_cmd,
198
+ x_timer,
199
+ x_write_warnings,
200
+ )
201
+
202
+ # Regex helper functions (from utils/regex.py)
203
+ from execsql.utils.regex import (
204
+ ins_fn_rxs,
205
+ ins_rxs,
206
+ ins_schema_rxs,
207
+ ins_table_list_rxs,
208
+ ins_table_rxs,
209
+ )
210
+ from execsql.script import MetaCommandList
211
+
212
+ # Format constants — imported from the package so this module and __init__.py
213
+ # share a single source of truth.
214
+ from execsql.metacommands import (
215
+ JSON_VARIANT_FORMATS,
216
+ METADATA_FORMATS,
217
+ QUERY_EXPORT_FORMATS,
218
+ SERVE_FORMATS,
219
+ TABLE_EXPORT_FORMATS,
220
+ )
221
+
222
+
223
+ def build_dispatch_table() -> MetaCommandList:
224
+ """Construct and return the complete metacommand dispatch table."""
225
+ mcl = MetaCommandList()
226
+
227
+ # ------------------------------------------------------------------
228
+ # DEBUG metacommands
229
+ # ------------------------------------------------------------------
230
+ mcl.add(
231
+ ins_fn_rxs(r"^\s*DEBUG\s+WRITE\s+METACOMMANDLIST\s+TO\s+", r"\s*$"),
232
+ x_debug_write_metacommands,
233
+ description="DEBUG",
234
+ category="action",
235
+ )
236
+ mcl.add(r"^\s*DEBUG\s+WRITE\s+COMMANDLISTSTACK\s*$", x_debug_commandliststack)
237
+ mcl.add(r"^\s*DEBUG\s+WRITE\s+IFLEVELS\s*$", x_debug_iflevels)
238
+ mcl.add(
239
+ ins_fn_rxs(
240
+ r"^\s*DEBUG\s+WRITE\s+ODBC_DRIVERS(?:\s+(?P<append>APPEND\s+)?TO\s+",
241
+ r")?\s*$",
242
+ ),
243
+ x_debug_write_odbc_drivers,
244
+ )
245
+ mcl.add(
246
+ r"^\s*DEBUG\s+LOG(?:\s+(?P<local>LOCAL))?(?:\s+(?P<user>USER))?\s+SUBVARS\s*$",
247
+ x_debug_log_subvars,
248
+ )
249
+ mcl.add(r"^\s*DEBUG\s+LOG\s+CONFIG\s*$", x_debug_log_config)
250
+ mcl.add(
251
+ ins_fn_rxs(
252
+ r"^\s*DEBUG\s+WRITE(?:\s+(?P<local>LOCAL))?(?:\s+(?P<user>USER))?\s+SUBVARS"
253
+ r"(?:\s+(?P<append>APPEND\s+)?TO\s+",
254
+ r")?\s*$",
255
+ ),
256
+ x_debug_write_subvars,
257
+ )
258
+ mcl.add(
259
+ ins_fn_rxs(
260
+ r"^\s*DEBUG\s+WRITE\s+CONFIG(?:\s+(?P<append>APPEND\s+)?TO\s+",
261
+ r")?\s*$",
262
+ ),
263
+ x_debug_write_config,
264
+ )
265
+
266
+ # ------------------------------------------------------------------
267
+ # SERVE
268
+ # ------------------------------------------------------------------
269
+ mcl.add(
270
+ ins_fn_rxs(
271
+ r"^\s*SERVE\s+",
272
+ rf"\s+AS\s+(?P<format>{'|'.join(SERVE_FORMATS)})\s*$",
273
+ ),
274
+ x_serve,
275
+ description="SERVE",
276
+ category="action",
277
+ )
278
+
279
+ # ------------------------------------------------------------------
280
+ # Misc short commands
281
+ # ------------------------------------------------------------------
282
+ mcl.add(
283
+ r"^\s*RESET\s+DIALOG_CANCELED\s*$",
284
+ x_reset_dialog_canceled,
285
+ description="RESET DIALOG_CANCELED",
286
+ category="action",
287
+ )
288
+ mcl.add(
289
+ r"^\s*SUB_QUERYSTRING\s+(?P<qstr>.+)\s*$",
290
+ x_sub_querystring,
291
+ description="SUB_QUERYSTRING",
292
+ category="action",
293
+ )
294
+ mcl.add(r"^\s*BREAK\s*$", x_break, description="BREAK", category="control")
295
+
296
+ # ------------------------------------------------------------------
297
+ # EXPORT QUERY (various formats)
298
+ # ------------------------------------------------------------------
299
+ mcl.add(
300
+ ins_fn_rxs(
301
+ r"^\s*EXPORT\s+QUERY\s+<<\s*(?P<query>.*;)\s*>>\s+(?P<tee>TEE\s+)?(?P<append>APPEND\s+)?TO\s+",
302
+ ins_fn_rxs(
303
+ r"(?:\s+IN\s+ZIPFILE\s+",
304
+ rf")?\s+AS\s*(?P<format>{'|'.join(QUERY_EXPORT_FORMATS)}|PARQUET|TXT-AND)"
305
+ r'(?:\s+DESCRIP(?:TION)?\s+"(?P<description>[^"]*)")?\s*$',
306
+ symbolicname="zipfilename",
307
+ ),
308
+ ),
309
+ x_export_query,
310
+ "EXPORT QUERY",
311
+ category="action",
312
+ )
313
+ mcl.add(
314
+ ins_fn_rxs(
315
+ r"^\s*EXPORT\s+QUERY\s+<<\s*(?P<query>.*;)\s*>>\s+(?P<tee>TEE\s+)?(?P<append>APPEND\s+)?TO\s+",
316
+ rf"\s+AS\s*(?P<format>{'|'.join(JSON_VARIANT_FORMATS)})(?:\s+(?P<notype>NOTYPE))?"
317
+ r'(?:\s+DESCRIP(?:TION)?\s+"(?P<description>[^"]*)")?\s*$',
318
+ ),
319
+ x_export_query,
320
+ )
321
+ mcl.add(
322
+ ins_fn_rxs(
323
+ r"^\s*EXPORT\s+QUERY\s+<<\s*(?P<query>.*;)\s*>>\s+(?P<tee>TEE\s+)?(?P<append>APPEND\s+)?TO\s+",
324
+ ins_fn_rxs(
325
+ r"(?:\s+IN\s+ZIPFILE\s+",
326
+ ins_fn_rxs(r")?\s+WITH\s+TEMPLATE\s+", r"\s*$", "template"),
327
+ symbolicname="zipfilename",
328
+ ),
329
+ ),
330
+ x_export_query_with_template,
331
+ )
332
+
333
+ # ------------------------------------------------------------------
334
+ # EXPORT (table/view)
335
+ # ------------------------------------------------------------------
336
+ mcl.add(
337
+ ins_table_rxs(
338
+ r"^\s*EXPORT\s+",
339
+ ins_fn_rxs(
340
+ r"\s+(?P<tee>TEE\s+)?(?P<append>APPEND\s+)?TO\s+",
341
+ ins_fn_rxs(
342
+ r"(?:\s+IN\s+ZIPFILE\s+",
343
+ ins_fn_rxs(r")?\s+WITH\s+TEMPLATE\s+", r"\s*$", "template"),
344
+ symbolicname="zipfilename",
345
+ ),
346
+ ),
347
+ ),
348
+ x_export_with_template,
349
+ description="EXPORT",
350
+ category="action",
351
+ )
352
+ mcl.add(
353
+ ins_table_rxs(
354
+ r"^\s*EXPORT\s+",
355
+ ins_fn_rxs(
356
+ ins_fn_rxs(
357
+ r"\s+(?P<tee>TEE\s+)?(?P<append>APPEND\s+)?TO\s+",
358
+ r"(?:\s+IN\s+ZIPFILE\s+",
359
+ ),
360
+ rf")?\s+AS\s+(?P<format>{'|'.join(TABLE_EXPORT_FORMATS)}|PARQUET|TXT-AND)"
361
+ r'(?:\s+DESCRIP(?:TION)?\s+"(?P<description>[^"]*)")?\s*$',
362
+ symbolicname="zipfilename",
363
+ ),
364
+ ),
365
+ x_export,
366
+ "EXPORT",
367
+ category="action",
368
+ )
369
+ mcl.add(
370
+ ins_table_rxs(
371
+ r"^\s*EXPORT\s+",
372
+ ins_fn_rxs(
373
+ ins_fn_rxs(
374
+ r"\s+(?P<tee>TEE\s+)?(?P<append>APPEND\s+)?TO\s+",
375
+ r"(?:\s+IN\s+ZIPFILE\s+",
376
+ ),
377
+ rf")?\s+AS\s+(?P<format>{'|'.join(JSON_VARIANT_FORMATS)})(?:\s+(?P<notype>NOTYPE))?"
378
+ r'(?:\s+DESCRIP(?:TION)?\s+"(?P<description>[^"]*)")?\s*$',
379
+ symbolicname="zipfilename",
380
+ ),
381
+ ),
382
+ x_export,
383
+ )
384
+ mcl.add(
385
+ ins_table_list_rxs(
386
+ r"^\s*EXPORT\s+",
387
+ ins_fn_rxs(
388
+ r"\s+(?P<tee>TEE\s+)?(?P<append>APPEND\s+)?TO\s+",
389
+ r'\s+AS\s+ODS(?:\s+DESCRIP(?:TION)?\s+"(?P<description>[^"]*)")?\s*$',
390
+ ),
391
+ ),
392
+ x_export_ods_multiple,
393
+ )
394
+
395
+ # ------------------------------------------------------------------
396
+ # IMPORT_FILE
397
+ # ------------------------------------------------------------------
398
+ mcl.add(
399
+ ins_table_rxs(
400
+ r"^\s*IMPORT_FILE\s+TO\s+TABLE\s+",
401
+ ins_fn_rxs(
402
+ r'\s+COLUMN\s+"(?P<columnname>[A-Za-z0-9_\-\: ]+)"\s+FROM\s+',
403
+ r"\s*$",
404
+ ),
405
+ ),
406
+ x_import_file,
407
+ description="IMPORT_FILE",
408
+ category="action",
409
+ )
410
+ mcl.add(
411
+ ins_table_rxs(
412
+ r"^\s*IMPORT_FILE\s+TO\s+TABLE\s+",
413
+ ins_fn_rxs(
414
+ r"\s+COLUMN\s+(?P<columnname>[A-Za-z0-9_\-\:]+)\s+FROM\s+",
415
+ r"\s*$",
416
+ ),
417
+ ),
418
+ x_import_file,
419
+ )
420
+
421
+ # ------------------------------------------------------------------
422
+ # IMPORT ODS (pattern)
423
+ # ------------------------------------------------------------------
424
+ mcl.add(
425
+ ins_schema_rxs(
426
+ r"\s*IMPORT\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?TABLES\s+IN\s+(?:SCHEMA\s+)?",
427
+ ins_fn_rxs(
428
+ r"\s+FROM\s+",
429
+ r"\s+SHEETS\s+MATCHING\s+(?P<patn>\S+)(?:\s+SKIP\s+(?P<skip>\d+))?\s*?",
430
+ ),
431
+ ),
432
+ x_import_ods_pattern,
433
+ description="IMPORT",
434
+ category="action",
435
+ )
436
+
437
+ # ------------------------------------------------------------------
438
+ # CD
439
+ # ------------------------------------------------------------------
440
+ mcl.add(r"^\s*CD\s+(?P<dir>.+)\s*$", x_cd, description="CD", category="action")
441
+
442
+ # ------------------------------------------------------------------
443
+ # IMPORT ODS (single sheet)
444
+ # ------------------------------------------------------------------
445
+ mcl.add(
446
+ ins_table_rxs(
447
+ r"^\s*IMPORT\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?",
448
+ ins_fn_rxs(
449
+ r"\s+FROM\s+",
450
+ r'\s+SHEET\s+"(?P<sheetname>[A-Za-z0-9_\.\/\-\\ ]+)"'
451
+ r"(?:\s+SKIP\s+(?P<skip>\d+))?\s*$",
452
+ ),
453
+ )
454
+ + ins_table_rxs(
455
+ r"^\s*IMPORT\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?",
456
+ ins_fn_rxs(
457
+ r"\s+FROM\s+",
458
+ r"\s+SHEET\s+(?P<sheetname>[A-Za-z0-9_\.\/\-\\]+)"
459
+ r"(?:\s+SKIP\s+(?P<skip>\d+))?\s*$",
460
+ ),
461
+ ),
462
+ x_import_ods,
463
+ )
464
+
465
+ # ------------------------------------------------------------------
466
+ # IMPORT XLS (pattern)
467
+ # ------------------------------------------------------------------
468
+ mcl.add(
469
+ ins_schema_rxs(
470
+ r"\s*IMPORT\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?TABLES\s+IN\s+(?:SCHEMA\s+)?",
471
+ ins_fn_rxs(
472
+ r"\s+FROM\s+EXCEL\s+",
473
+ r"\s+SHEETS\s+MATCHING\s+(?P<patn>\S+)(?:\s+SKIP\s+(?P<skip>\d+))?"
474
+ r"(?:\s+ENCODING\s+(?P<encoding>\w+))?\s*?",
475
+ ),
476
+ ),
477
+ x_import_xls_pattern,
478
+ )
479
+
480
+ # ------------------------------------------------------------------
481
+ # IMPORT XLS (single sheet)
482
+ # ------------------------------------------------------------------
483
+ mcl.add(
484
+ ins_table_rxs(
485
+ r"^\s*IMPORT\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?",
486
+ ins_fn_rxs(
487
+ r"\s+FROM\s+EXCEL\s+",
488
+ r'\s+SHEET\s+"(?P<sheetname>[A-Za-z0-9_\.\/\-\\ ]+)"'
489
+ r"(?:\s+SKIP\s+(?P<skip>\d+))?(?:\s+ENCODING\s+(?P<encoding>\w+))?\s*$",
490
+ ),
491
+ )
492
+ + ins_table_rxs(
493
+ r"^\s*IMPORT\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?",
494
+ ins_fn_rxs(
495
+ r"\s+FROM\s+EXCEL\s+",
496
+ r"\s+SHEET\s+(?P<sheetname>[A-Za-z0-9_\.\/\-\\]+)"
497
+ r"(?:\s+SKIP\s+(?P<skip>\d+))?(?:\s+ENCODING\s+(?P<encoding>\w+))?\s*$",
498
+ ),
499
+ ),
500
+ x_import_xls,
501
+ )
502
+
503
+ # ------------------------------------------------------------------
504
+ # IMPORT PARQUET / FEATHER
505
+ # ------------------------------------------------------------------
506
+ mcl.add(
507
+ ins_table_rxs(
508
+ r"^\s*IMPORT\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?",
509
+ ins_fn_rxs(r"\s+FROM\s+PARQUET\s+", r"\s*$"),
510
+ ),
511
+ x_import_parquet,
512
+ )
513
+ mcl.add(
514
+ ins_table_rxs(
515
+ r"^\s*IMPORT\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?",
516
+ ins_fn_rxs(r"\s+FROM\s+FEATHER\s+", r"\s*$"),
517
+ ),
518
+ x_import_feather,
519
+ )
520
+
521
+ # ------------------------------------------------------------------
522
+ # PROMPT ACTION
523
+ # ------------------------------------------------------------------
524
+ mcl.add(
525
+ ins_table_rxs(
526
+ r"^\s*PROMPT\s+ACTION\s+",
527
+ ins_table_rxs(
528
+ r'\s+MESSAGE\s+"(?P<message>(.|\n)*)"(?:\s+DISPLAY\s+',
529
+ r")?(?:\s+COMPACT\s+(?P<compact>\d+))?(?:\s+(?P<continue>CONTINUE))?"
530
+ r"(?:\s+HELP\s+(?P<help>[^\s]+))?\s*$",
531
+ suffix="disp",
532
+ ),
533
+ ),
534
+ x_prompt_action,
535
+ description="PROMPT ACTION",
536
+ category="prompt",
537
+ )
538
+ mcl.add(
539
+ ins_table_rxs(
540
+ r"^\s*PROMPT\s+ACTION\s+",
541
+ ins_table_rxs(
542
+ r'\s+MESSAGE\s+"(?P<message>(.|\n)*)"(?:\s+DISPLAY\s+',
543
+ r")?(?:\s+COMPACT\s+(?P<compact>\d+))?(?:\s+(?P<continue>CONTINUE))?"
544
+ r'(?:\s+HELP\s+"(?P<help>[^"]+)")?\s*$',
545
+ suffix="disp",
546
+ ),
547
+ ),
548
+ x_prompt_action,
549
+ )
550
+
551
+ # ------------------------------------------------------------------
552
+ # PROMPT SAVEFILE / OPENFILE / DIRECTORY
553
+ # ------------------------------------------------------------------
554
+ mcl.add(
555
+ ins_fn_rxs(
556
+ r"^\s*PROMPT\s+SAVEFILE\s+SUB\s+(?P<match>~?\w+)(?:\s+(?P<fn_match>~?\w+))?"
557
+ r"(?:\s+(?P<path_match>~?\w+))?(?:\s+(?P<ext_match>~?\w+))?"
558
+ r"(?:\s+(?P<fnbase_match>~?\w+))?(?:\s+FROM\s+",
559
+ r")?\s*$",
560
+ symbolicname="startdir",
561
+ ),
562
+ x_prompt_savefile,
563
+ description="PROMPT SAVEFILE",
564
+ category="prompt",
565
+ )
566
+ mcl.add(
567
+ ins_fn_rxs(
568
+ r"^\s*PROMPT\s+OPENFILE\s+SUB\s+(?P<match>~?\w+)(?:\s+(?P<fn_match>~?\w+))?"
569
+ r"(?:\s+(?P<path_match>~?\w+))?(?:\s+(?P<ext_match>~?\w+))?"
570
+ r"(?:\s+(?P<fnbase_match>~?\w+))?(?:\s+FROM\s+",
571
+ r")?\s*$",
572
+ symbolicname="startdir",
573
+ ),
574
+ x_prompt_openfile,
575
+ description="PROMPT OPENFILE",
576
+ category="prompt",
577
+ )
578
+ mcl.add(
579
+ ins_fn_rxs(
580
+ r"^\s*PROMPT\s+DIRECTORY\s+SUB\s+(?P<match>~?\w+)(?:\s+(?P<fullpath>FULLPATH))?"
581
+ r"(?:\s+FROM\s+",
582
+ r")?\s*$",
583
+ symbolicname="startdir",
584
+ ),
585
+ x_prompt_directory,
586
+ description="PROMPT DIRECTORY",
587
+ category="prompt",
588
+ )
589
+
590
+ # ------------------------------------------------------------------
591
+ # PROMPT SELECT_ROWS
592
+ # ------------------------------------------------------------------
593
+ mcl.add(
594
+ ins_table_rxs(
595
+ r"^\s*PROMPT\s+SELECT_ROWS\s+FROM\s+",
596
+ ins_table_rxs(
597
+ r"(?:\s+IN\s+(?P<alias1>\w+))?\s+INTO\s+",
598
+ r'(?:\s+IN\s+(?P<alias2>\w+))?(?:\s+HELP\s+(?P<help>[^\s]+))?\s+MESSAGE\s+"(?P<msg>(.|\n)*)"\s*$',
599
+ suffix="2",
600
+ ),
601
+ suffix="1",
602
+ ),
603
+ x_prompt_select_rows,
604
+ description="PROMPT SELECT_ROWS",
605
+ category="prompt",
606
+ )
607
+ mcl.add(
608
+ ins_table_rxs(
609
+ r"^\s*PROMPT\s+SELECT_ROWS\s+FROM\s+",
610
+ ins_table_rxs(
611
+ r"(?:\s+IN\s+(?P<alias1>\w+))?\s+INTO\s+",
612
+ r'(?:\s+IN\s+(?P<alias2>\w+))?(?:\s+HELP\s+"(?P<help>[^"]+)")?\s+MESSAGE\s+"(?P<msg>(.|\n)*)"\s*$',
613
+ suffix="2",
614
+ ),
615
+ suffix="1",
616
+ ),
617
+ x_prompt_select_rows,
618
+ )
619
+
620
+ # ------------------------------------------------------------------
621
+ # SUB_LOCAL / SUB_ENCRYPT / SUB_DECRYPT
622
+ # ------------------------------------------------------------------
623
+ mcl.add(
624
+ r"^\s*SUB_LOCAL\s+(?P<match>~?\w+)\s+(?P<repl>.+)$",
625
+ x_sub_local,
626
+ description="SUB_LOCAL",
627
+ category="action",
628
+ )
629
+ mcl.add(
630
+ r"^\s*SUB_ENCRYPT\s+(?P<match>[+~]?\w+)\s+(?P<plaintext>.+)\s*$",
631
+ x_sub_encrypt,
632
+ description="SUB_ENCRYPT",
633
+ category="action",
634
+ )
635
+ mcl.add(
636
+ r"^\s*SUB_DECRYPT\s+(?P<match>[+~]?\w+)\s+(?P<crypttext>.+)\s*$",
637
+ x_sub_decrypt,
638
+ description="SUB_DECRYPT",
639
+ category="action",
640
+ )
641
+
642
+ # ------------------------------------------------------------------
643
+ # WAIT_UNTIL
644
+ # ------------------------------------------------------------------
645
+ mcl.add(
646
+ r"^\s*WAIT_UNTIL\s+(?P<condition>.+)\s+(?P<end>HALT|CONTINUE)\s+AFTER\s+(?P<seconds>\d+)\s+SECONDS\s*$",
647
+ x_wait_until,
648
+ description="WAIT_UNTIL",
649
+ category="control",
650
+ )
651
+
652
+ # ------------------------------------------------------------------
653
+ # CONFIG * (various settings)
654
+ # ------------------------------------------------------------------
655
+ mcl.add(
656
+ r"^\s*LOG_WRITE_MESSAGES\s+(?P<setting>Yes|No|On|Off|True|False|0|1)\s*$",
657
+ x_logwritemessages,
658
+ description="LOG_WRITE_MESSAGES",
659
+ category="config_option",
660
+ )
661
+ mcl.add(
662
+ r"^\s*CONFIG\s+LOG_WRITE_MESSAGES\s+(?P<setting>Yes|No|On|Off|True|False|0|1)\s*$",
663
+ x_logwritemessages,
664
+ description="CONFIG",
665
+ category="config",
666
+ )
667
+ mcl.add(
668
+ r"^\s*CONFIG\s+QUOTE_ALL_TEXT\s+(?P<setting>Yes|No|On|Off|True|False|0|1)\s*$",
669
+ x_quote_all_text,
670
+ description="QUOTE_ALL_TEXT",
671
+ category="config_option",
672
+ )
673
+ mcl.add(
674
+ r"^\s*CONFIG\s+IMPORT_ROW_BUFFER\s+(?P<rows>[1-9][0-9]*)\s*$",
675
+ x_import_row_buffer,
676
+ description="IMPORT_ROW_BUFFER",
677
+ category="config_option",
678
+ )
679
+ mcl.add(
680
+ r"^\s*CONFIG\s+SHOW_PROGRESS\s+(?P<setting>Yes|No|On|Off|True|False|0|1)\s*$",
681
+ x_show_progress,
682
+ description="SHOW_PROGRESS",
683
+ category="config_option",
684
+ )
685
+ mcl.add(
686
+ r"^\s*CONFIG\s+EXPORT_ROW_BUFFER\s+(?P<rows>[1-9][0-9]*)\s*$",
687
+ x_export_row_buffer,
688
+ description="EXPORT_ROW_BUFFER",
689
+ category="config_option",
690
+ )
691
+ mcl.add(
692
+ r"^\s*CONFIG\s+ZIP_BUFFER_MB\s+(?P<size>[1-9][0-9]*)\s*$",
693
+ x_zip_buffer_mb,
694
+ description="ZIP_BUFFER_MB",
695
+ category="config_option",
696
+ )
697
+ mcl.add(
698
+ r"^\s*EMPTY_STRINGS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
699
+ x_empty_strings,
700
+ description="EMPTY_STRINGS",
701
+ category="config_option",
702
+ )
703
+ mcl.add(
704
+ r"^\s*CONFIG\s+EMPTY_STRINGS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
705
+ x_empty_strings,
706
+ )
707
+ mcl.add(
708
+ r"^\s*TRIM_STRINGS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
709
+ x_trim_strings,
710
+ description="TRIM_STRINGS",
711
+ category="config_option",
712
+ )
713
+ mcl.add(
714
+ r"^\s*CONFIG\s+TRIM_STRINGS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
715
+ x_trim_strings,
716
+ )
717
+ mcl.add(
718
+ r"^\s*CONFIG\s+REPLACE_NEWLINES\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
719
+ x_replace_newlines,
720
+ description="REPLACE_NEWLINES",
721
+ category="config_option",
722
+ )
723
+ mcl.add(
724
+ r"^\s*CONFIG\s+EMPTY_ROWS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
725
+ x_empty_rows,
726
+ description="EMPTY_ROWS",
727
+ category="config_option",
728
+ )
729
+ mcl.add(
730
+ r"^\s*CONFIG\s+ONLY_STRINGS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
731
+ x_only_strings,
732
+ description="ONLY_STRINGS",
733
+ category="config_option",
734
+ )
735
+ mcl.add(
736
+ r"^\s*(?:CONFIG\s+)?BOOLEAN_INT\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
737
+ x_boolean_int,
738
+ description="BOOLEAN_INT",
739
+ category="config_option",
740
+ )
741
+ mcl.add(
742
+ r"^\s*BOOLEAN_WORDS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
743
+ x_boolean_words,
744
+ description="BOOLEAN_WORDS",
745
+ category="config_option",
746
+ )
747
+ mcl.add(
748
+ r"^\s*CONFIG\s+BOOLEAN_WORDS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
749
+ x_boolean_words,
750
+ )
751
+ mcl.add(
752
+ r"^\s*CONFIG\s+FOLD_COLUMN_HEADERS\s+(?P<foldspec>no|lower|upper)\s*$",
753
+ x_fold_col_hdrs,
754
+ description="FOLD_COLUMN_HEADERS",
755
+ category="config_option",
756
+ )
757
+ mcl.add(
758
+ r"^\s*CONFIG\s+TRIM_COLUMN_HEADERS\s+(?P<which>NONE|BOTH|LEFT|RIGHT)\s*$",
759
+ x_trim_col_hdrs,
760
+ description="TRIM_COLUMN_HEADERS",
761
+ category="config_option",
762
+ )
763
+ mcl.add(
764
+ r"^\s*CLEAN_COLUMN_HEADERS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
765
+ x_clean_col_hdrs,
766
+ description="CLEAN_COLUMN_HEADERS",
767
+ category="config_option",
768
+ )
769
+ mcl.add(
770
+ r"^\s*CONFIG\s+CLEAN_COLUMN_HEADERS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
771
+ x_clean_col_hdrs,
772
+ )
773
+ mcl.add(
774
+ r"^\s*CONFIG\s+DELETE_EMPTY_COLUMNS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
775
+ x_del_empty_cols,
776
+ description="DELETE_EMPTY_COLUMNS",
777
+ category="config_option",
778
+ )
779
+ mcl.add(
780
+ r"^\s*CONFIG\s+CREATE_COLUMN_HEADERS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
781
+ x_create_col_hdrs,
782
+ description="CREATE_COLUMN_HEADERS",
783
+ category="config_option",
784
+ )
785
+ mcl.add(
786
+ r"^\s*CONFIG\s+DEDUP_COLUMN_HEADERS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
787
+ x_dedup_col_hdrs,
788
+ description="DEDUP_COLUMN_HEADERS",
789
+ category="config_option",
790
+ )
791
+ mcl.add(
792
+ r"^\s*IMPORT_ONLY_COMMON_COLUMNS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
793
+ x_import_common_cols_only,
794
+ description="IMPORT_ONLY_COMMON_COLUMNS",
795
+ category="config_option",
796
+ )
797
+ mcl.add(
798
+ r"^\s*IMPORT_COMMON_COLUMNS_ONLY\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
799
+ x_import_common_cols_only,
800
+ )
801
+ mcl.add(
802
+ r"^\s*CONFIG\s+IMPORT_ONLY_COMMON_COLUMNS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
803
+ x_import_common_cols_only,
804
+ )
805
+ mcl.add(
806
+ r"^\s*MAKE_EXPORT_DIRS\s+(?P<setting>Yes|No|On|Off|True|False|0|1)\s*$",
807
+ x_make_export_dirs,
808
+ description="MAKE_EXPORT_DIRS",
809
+ category="config_option",
810
+ )
811
+ mcl.add(
812
+ r"^\s*CONFIG\s+MAKE_EXPORT_DIRS\s+(?P<setting>Yes|No|On|Off|True|False|0|1)\s*$",
813
+ x_make_export_dirs,
814
+ )
815
+ mcl.add(
816
+ r"^\s*WRITE_WARNINGS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
817
+ x_write_warnings,
818
+ description="WRITE_WARNINGS",
819
+ category="config_option",
820
+ )
821
+ mcl.add(
822
+ r"^\s*CONFIG\s+WRITE_WARNINGS\s+(?P<yesno>YES|NO|ON|OFF|TRUE|FALSE|0|1)\s*$",
823
+ x_write_warnings,
824
+ )
825
+ mcl.add(
826
+ r"^\s*CONFIG\s+GUI_LEVEL\s+(?P<level>[0-2])\s*$",
827
+ x_gui_level,
828
+ description="GUI_LEVEL",
829
+ category="config_option",
830
+ )
831
+ mcl.add(
832
+ r"^\s*CONFIG\s+WRITE_PREFIX\s+(?P<prefix>.*)\s*$",
833
+ x_write_prefix,
834
+ description="WRITE_PREFIX",
835
+ category="config_option",
836
+ )
837
+ mcl.add(
838
+ r"^\s*CONFIG\s+WRITE_SUFFIX\s+(?P<suffix>.*)\s*$",
839
+ x_write_suffix,
840
+ description="WRITE_SUFFIX",
841
+ category="config_option",
842
+ )
843
+ mcl.add(
844
+ r"^\s*CONFIG\s+SCAN_LINES\s+(?P<scanlines>[0-9]+)\s*$",
845
+ x_scan_lines,
846
+ description="SCAN_LINES",
847
+ category="config_option",
848
+ )
849
+ mcl.add(
850
+ r"^\s*CONFIG\s+HDF5_TEXT_LEN\s+(?P<textlen>[0-9]+)\s*$",
851
+ x_hdf5_text_len,
852
+ description="HDF5_TEXT_LEN",
853
+ category="config_option",
854
+ )
855
+ mcl.add(
856
+ r"^\s*CONFIG\s+LOG_DATAVARS\s+(?P<setting>Yes|No|On|Off|True|False|0|1)\s*$",
857
+ x_log_datavars,
858
+ description="LOG_DATAVARS",
859
+ category="config_option",
860
+ )
861
+ mcl.add(
862
+ r"^\s*CONFIG\s+LOG_SQL\s+(?P<setting>Yes|No|On|Off|True|False|0|1)\s*$",
863
+ x_log_sql,
864
+ description="LOG_SQL",
865
+ category="config_option",
866
+ )
867
+ mcl.add(
868
+ r"^\s*CONFIG\s+DAO_FLUSH_DELAY_SECS\s+(?P<secs>[0-9]*\.?[0-9]+)\s*$",
869
+ x_daoflushdelay,
870
+ description="DAO_FLUSH_DELAY_SECS",
871
+ category="config_option",
872
+ )
873
+ mcl.add(
874
+ r"^\s*CONSOLE\s+WAIT_WHEN_ERROR\s+(?P<onoff>ON|OFF|YES|NO|TRUE|FALSE|0|1)\s*$",
875
+ x_consolewait_onerror,
876
+ description="CONSOLE_WAIT_WHEN_ERROR",
877
+ category="config_option",
878
+ )
879
+ mcl.add(
880
+ r"^\s*CONFIG\s+CONSOLE\s+WAIT_WHEN_ERROR\s+(?P<onoff>ON|OFF|YES|NO|TRUE|FALSE|0|1)\s*$",
881
+ x_consolewait_onerror,
882
+ )
883
+ mcl.add(
884
+ r"^\s*CONSOLE\s+WAIT_WHEN_DONE\s+(?P<onoff>ON|OFF|YES|NO|TRUE|FALSE|0|1)\s*$",
885
+ x_consolewait_whendone,
886
+ description="CONSOLE_WAIT_WHEN_DONE",
887
+ category="config_option",
888
+ )
889
+ mcl.add(
890
+ r"^\s*CONFIG\s+CONSOLE\s+WAIT_WHEN_DONE\s+(?P<onoff>ON|OFF|YES|NO|TRUE|FALSE|0|1)\s*$",
891
+ x_consolewait_whendone,
892
+ )
893
+
894
+ # ------------------------------------------------------------------
895
+ # CONNECT — MS Access
896
+ # ------------------------------------------------------------------
897
+ mcl.add(
898
+ ins_fn_rxs(
899
+ r"^CONNECT\s+TO\s+ACCESS\s*\(\s*FILE\s*=\s*",
900
+ r"(?:\s*,\s*NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?"
901
+ r"(?:\s*,\s+PASSWORD\s*=\s*(?P<password>[^\s]+))?"
902
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
903
+ ),
904
+ x_connect_access,
905
+ description="CONNECT",
906
+ category="action",
907
+ )
908
+
909
+ # ------------------------------------------------------------------
910
+ # CONNECT — Firebird
911
+ # ------------------------------------------------------------------
912
+ mcl.add(
913
+ (
914
+ r"^CONNECT\s+USER\s+TO\s+FIREBIRD\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
915
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
916
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
917
+ r'^CONNECT\s+USER\s+TO\s+FIREBIRD\s*\(\s*SERVER\s*=\s*"(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)"\s*,\s*'
918
+ r'DB\s*=\s*"(?P<db_name>[A-Z][A-Z0-9_\- ]*)"(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?'
919
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
920
+ ),
921
+ x_connect_user_fb,
922
+ )
923
+ mcl.add(
924
+ r"^CONNECT\s+TO\s+FIREBIRD\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
925
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*USER\s*=\s*(?P<user>[A-Z][A-Z0-9_@\-\.]*)\s*,\s*"
926
+ r"NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
927
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
928
+ x_connect_fb,
929
+ )
930
+
931
+ # ------------------------------------------------------------------
932
+ # CONNECT — Oracle
933
+ # ------------------------------------------------------------------
934
+ mcl.add(
935
+ (
936
+ r"^CONNECT\s+USER\s+TO\s+ORACLE\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
937
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
938
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
939
+ r'^CONNECT\s+USER\s+TO\s+ORACLE\s*\(\s*SERVER\s*=\s*"(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)"\s*,\s*'
940
+ r'DB\s*=\s*"(?P<db_name>[A-Z][A-Z0-9_\- ]*)"(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?'
941
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
942
+ ),
943
+ x_connect_user_ora,
944
+ )
945
+ mcl.add(
946
+ (
947
+ r"^CONNECT\s+TO\s+ORACLE\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
948
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*USER\s*=\s*(?P<user>[A-Z][A-Z0-9_\-@\.]*)\s*,\s*"
949
+ r"NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
950
+ r"(?:\s*,\s+PASSWORD\s*=\s*(?P<password>[^\s\)]+))?"
951
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
952
+ r'^CONNECT\s+TO\s+ORACLE\s*\(\s*SERVER\s*=\s*"(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)"\s*,\s*'
953
+ r'DB\s*=\s*"(?P<db_name>[A-Z][A-Z0-9_\-]*)"(?:\s*,\s*USER\s*=\s*"(?P<user>[A-Z][A-Z0-9_\-@\.]*)"\s*,\s*'
954
+ r"NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
955
+ r'(?:\s*,\s+PASSWORD\s*=\s*"(?P<password>[^\s\)]+)")?'
956
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
957
+ ),
958
+ x_connect_ora,
959
+ )
960
+
961
+ # ------------------------------------------------------------------
962
+ # RUN / EXECUTE
963
+ # ------------------------------------------------------------------
964
+ mcl.add(
965
+ r'^\s*EXEC(?:UTE)?\s+SCRIPT(?:\s+(?P<exists>IF\s+EXISTS))?\s+(?P<script_id>\w+)(?:(?:\s+WITH)?(?:\s+ARG(?:UMENT)?S?)?\s*\(\s*(?P<argexp>#?\w+\s*=\s*(?:(?:[^"\'\[][^,\)]*)|(?:"[^"]*")|(?:\'[^\']*\')|(?:\[[^\]]*\]))(?:\s*,\s*#?\w+\s*=\s*(?:(?:[^"\'\[][^,\)]*)|(?:"[^"]*")|(?:\'[^\']*\')|(?:\[[^\]]*\])))*)\s*\))?(?:\s+(?P<looptype>WHILE|UNTIL)\s*\(\s*(?P<loopcond>.+)\s*\))?\s*$',
966
+ x_executescript,
967
+ description="EXECUTE SCRIPT",
968
+ category="action",
969
+ )
970
+ mcl.add(
971
+ r'^\s*RUN\s+SCRIPT(?:\s+(?P<exists>IF\s+EXISTS))?\s+(?P<script_id>\w+)(?:(?:\s+WITH)?(?:\s+ARG(?:UMENT)?S?)?\s*\(\s*(?P<argexp>#?\w+\s*=\s*(?:(?:[^"\'\[][^,\)]*)|(?:"[^"]*")|(?:\'[^\']*\')|(?:\[[^\]]*\]))(?:\s*,\s*#?\w+\s*=\s*(?:(?:[^"\'\[][^,\)]*)|(?:"[^"]*")|(?:\'[^\']*\')|(?:\[[^\]]*\])))*)\s*\))?(?:\s+(?P<looptype>WHILE|UNTIL)\s*\(\s*(?P<loopcond>.+)\s*\))?\s*$',
972
+ x_executescript,
973
+ description="RUN",
974
+ category="action",
975
+ )
976
+ mcl.add(
977
+ r"^\s*(?P<cmd>RUN|EXECUTE)\s+(?P<queryname>\#?\w+)\s*$",
978
+ x_execute,
979
+ description="RUN",
980
+ category="action",
981
+ )
982
+
983
+ # ------------------------------------------------------------------
984
+ # ON ERROR_HALT / ON CANCEL_HALT
985
+ # ------------------------------------------------------------------
986
+ mcl.add(
987
+ r"^\s*ON\s+ERROR_HALT\s+WRITE\s+CLEAR\s*$",
988
+ x_error_halt_write_clear,
989
+ description="ON ERROR_HALT",
990
+ category="config",
991
+ )
992
+ mcl.add(
993
+ ins_fn_rxs(
994
+ r"^\s*ON\s+ERROR_HALT\s+WRITE\s+\'(?P<text>([^\']|\n)*)\'(?:(?:\s+(?P<tee>TEE))?\s+TO\s+",
995
+ r")?\s*$",
996
+ ),
997
+ x_error_halt_write,
998
+ )
999
+ mcl.add(
1000
+ ins_fn_rxs(
1001
+ r"^\s*ON\s+ERROR_HALT\s+WRITE\s+\[(?P<text>([^\]]|\n)*)\](?:(?:\s+(?P<tee>TEE))?\s+TO\s+",
1002
+ r")?\s*$",
1003
+ ),
1004
+ x_error_halt_write,
1005
+ )
1006
+ mcl.add(
1007
+ ins_fn_rxs(
1008
+ r'^\s*ON\s+ERROR_HALT\s+WRITE\s+"(?P<text>([^"]|\n)*)"(?:(?:\s+(?P<tee>TEE))?\s+TO\s+',
1009
+ r")?\s*$",
1010
+ ),
1011
+ x_error_halt_write,
1012
+ )
1013
+ mcl.add(r"^\s*ON\s+ERROR_HALT\s+EMAIL\s+CLEAR\s*$", x_error_halt_email_clear)
1014
+ mcl.add(
1015
+ ins_fn_rxs(
1016
+ ins_fn_rxs(
1017
+ r"^\s*ON\s+ERROR_HALT\s+EMAIL\s+"
1018
+ r"FROM\s+(?P<from>[A-Za-z0-9_\-\.!#$%&\'*+/=?^`{|}~]+@[A-Za-z0-9]+(-[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)*)\s+"
1019
+ r"TO\s+(?P<to>[A-Za-z0-9_\-\.!#$%&\'*+/=?^`{|}~]+@[A-Za-z0-9]+(-[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)*"
1020
+ r"([;,]\s*[A-Za-z0-9\-\.!#$%&\'*+/=?^`{|}~]+@[A-Za-z0-9]+(-[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)*)*)\s+"
1021
+ r'SUBJECT "(?P<subject>[^"]+)"\s+'
1022
+ r'MESSAGE\s+"(?P<msg>[^"]*)"'
1023
+ r"(\s+MESSAGE_FILE\s+",
1024
+ r")?(\s+ATTACH(MEANT)?_FILE\s+",
1025
+ "msg_file",
1026
+ ),
1027
+ r")?\s*$",
1028
+ "att_file",
1029
+ ),
1030
+ x_error_halt_email,
1031
+ )
1032
+ mcl.add(r"^\s*ON\s+ERROR_HALT\s+EXEC\s+CLEAR\s*$", x_error_halt_exec_clear)
1033
+ mcl.add(
1034
+ r"^\s*ON\s+CANCEL_HALT\s+WRITE\s+CLEAR\s*$",
1035
+ x_cancel_halt_write_clear,
1036
+ description="ON CANCEL_HALT",
1037
+ category="config",
1038
+ )
1039
+ mcl.add(
1040
+ ins_fn_rxs(
1041
+ r"^\s*ON\s+CANCEL_HALT\s+WRITE\s+\'(?P<text>([^\']|\n)*)\'(?:(?:\s+(?P<tee>TEE))?\s+TO\s+",
1042
+ r")?\s*$",
1043
+ ),
1044
+ x_cancel_halt_write,
1045
+ )
1046
+ mcl.add(
1047
+ ins_fn_rxs(
1048
+ r"^\s*ON\s+CANCEL_HALT\s+WRITE\s+\[(?P<text>([^\]]|\n)*)\](?:(?:\s+(?P<tee>TEE))?\s+TO\s+",
1049
+ r")?\s*$",
1050
+ ),
1051
+ x_cancel_halt_write,
1052
+ )
1053
+ mcl.add(
1054
+ ins_fn_rxs(
1055
+ r'^\s*ON\s+CANCEL_HALT\s+WRITE\s+"(?P<text>([^"]|\n)*)"(?:(?:\s+(?P<tee>TEE))?\s+TO\s+',
1056
+ r")?\s*$",
1057
+ ),
1058
+ x_cancel_halt_write,
1059
+ )
1060
+ mcl.add(r"^\s*ON\s+CANCEL_HALT\s+EMAIL\s+CLEAR\s*$", x_cancel_halt_email_clear)
1061
+ mcl.add(
1062
+ ins_fn_rxs(
1063
+ ins_fn_rxs(
1064
+ r"^\s*ON\s+CANCEL_HALT\s+EMAIL\s+"
1065
+ r"FROM\s+(?P<from>[A-Za-z0-9_\-\.!#$%&\'*+/=?^`{|}~]+@[A-Za-z0-9]+(-[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)*)\s+"
1066
+ r"TO\s+(?P<to>[A-Za-z0-9_\-\.!#$%&\'*+/=?^`{|}~]+@[A-Za-z0-9]+(-[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)*"
1067
+ r"([;,]\s*[A-Za-z0-9\-\.!#$%&\'*+/=?^`{|}~]+@[A-Za-z0-9]+(-[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)*)*)\s+"
1068
+ r'SUBJECT "(?P<subject>[^"]+)"\s+'
1069
+ r'MESSAGE\s+"(?P<msg>[^"]*)"'
1070
+ r"(\s+MESSAGE_FILE\s+",
1071
+ r")?(\s+ATTACH(MEANT)?_FILE\s+",
1072
+ "msg_file",
1073
+ ),
1074
+ r")?\s*$",
1075
+ "att_file",
1076
+ ),
1077
+ x_cancel_halt_email,
1078
+ )
1079
+ mcl.add(r"^\s*ON\s+CANCEL_HALT\s+EXEC\s+CLEAR\s*$", x_cancel_halt_exec_clear)
1080
+
1081
+ # ------------------------------------------------------------------
1082
+ # SUB_TEMPFILE
1083
+ # ------------------------------------------------------------------
1084
+ mcl.add(
1085
+ r"^\s*SUB_TEMPFILE\s+(?P<match>[+~]?\w+)\s*$",
1086
+ x_sub_tempfile,
1087
+ description="SUB_TEMPFILE",
1088
+ category="action",
1089
+ )
1090
+
1091
+ # ------------------------------------------------------------------
1092
+ # WRITE CREATE_TABLE (ODS / XLS / CSV / alias)
1093
+ # ------------------------------------------------------------------
1094
+ mcl.add(
1095
+ ins_table_rxs(
1096
+ r"^\s*WRITE\s+CREATE_TABLE\s+",
1097
+ ins_fn_rxs(
1098
+ r"\s+FROM\s+",
1099
+ ins_rxs(
1100
+ (
1101
+ r'"(?P<sheet>[A-Za-z0-9_\.\/\-\\ ]+)"',
1102
+ r"(?P<sheet>[A-Za-z0-9_\.\/\-\\]+)",
1103
+ ),
1104
+ r"\s+SHEET\s+",
1105
+ ins_fn_rxs(
1106
+ r'(?:\s+SKIP\s+(?P<skip>\d+))?(?:\s+COMMENT\s+"(?P<comment>[^"]+)")?'
1107
+ r"(?:\s+TO\s+",
1108
+ r")?\s*$",
1109
+ "outfile",
1110
+ ),
1111
+ ),
1112
+ ),
1113
+ ),
1114
+ x_write_create_table_ods,
1115
+ description="WRITE CREATE_TABLE",
1116
+ category="action",
1117
+ )
1118
+ mcl.add(
1119
+ ins_table_rxs(
1120
+ r"^\s*WRITE\s+CREATE_TABLE\s+",
1121
+ ins_fn_rxs(
1122
+ r"\s+FROM\s+EXCEL\s+",
1123
+ ins_rxs(
1124
+ (
1125
+ r'"(?P<sheet>[A-Za-z0-9_\.\/\-\\ ]+)"',
1126
+ r"(?P<sheet>[A-Za-z0-9_\.\/\-\\]+)",
1127
+ ),
1128
+ r"\s+SHEET\s+",
1129
+ ins_fn_rxs(
1130
+ r"(?:\s+SKIP\s+(?P<skip>\d+))?(?:\s+ENCODING\s+(?P<encoding>\w+))?"
1131
+ r'(?:\s+COMMENT\s+"(?P<comment>[^"]+)")?(?:\s+TO\s+',
1132
+ r")?\s*$",
1133
+ "outfile",
1134
+ ),
1135
+ ),
1136
+ ),
1137
+ ),
1138
+ x_write_create_table_xls,
1139
+ )
1140
+ mcl.add(
1141
+ ins_table_rxs(
1142
+ r"^\s*WRITE\s+CREATE_TABLE\s+",
1143
+ ins_table_rxs(
1144
+ r"\s+FROM\s+",
1145
+ ins_fn_rxs(
1146
+ r'\s+IN\s+(?P<alias>[A-Z][A-Z0-9_]*)(?:\s+COMMENT\s+"(?P<comment>[^"]+)")?(?:\s+TO\s+',
1147
+ r")?\s*$",
1148
+ ),
1149
+ "1",
1150
+ ),
1151
+ ),
1152
+ x_write_create_table_alias,
1153
+ )
1154
+ mcl.add(
1155
+ ins_table_rxs(
1156
+ r"^\s*WRITE\s+CREATE_TABLE\s+",
1157
+ ins_fn_rxs(
1158
+ r"\s+FROM\s+",
1159
+ ins_fn_rxs(
1160
+ r'(?:\s+WITH\s+QUOTE\s+(?P<quotechar>NONE|\'|")\s+DELIMITER\s+(?P<delimchar>TAB|UNITSEP|US|,|;|\|))?'
1161
+ r"(?:\s+ENCODING\s+(?P<encoding>\w+))?(?:\s+SKIP\s+(?P<skip>\d+))?"
1162
+ r'(?:\s+COMMENT\s+"(?P<comment>[^"]+)")?(?:\s+TO\s+',
1163
+ r")?\s*$",
1164
+ "outfile",
1165
+ ),
1166
+ ),
1167
+ ),
1168
+ x_write_create_table,
1169
+ )
1170
+
1171
+ # ------------------------------------------------------------------
1172
+ # RESET / SET COUNTER
1173
+ # ------------------------------------------------------------------
1174
+ mcl.add(
1175
+ r"^\s*RESET\s+COUNTER\s+(?P<counter_no>\d+)\s*$",
1176
+ x_reset_counter,
1177
+ description="RESET COUNTER",
1178
+ category="action",
1179
+ )
1180
+ mcl.add(r"^\s*RESET\s+COUNTERS\s*$", x_reset_counters)
1181
+ mcl.add(
1182
+ r"^\s*SET\s+COUNTER\s+(?P<counter_no>\d+)\s+TO\s+(?P<value>[0-9+\-*/() ]+)\s*$",
1183
+ x_set_counter,
1184
+ description="SET COUNTER",
1185
+ category="action",
1186
+ )
1187
+
1188
+ # ------------------------------------------------------------------
1189
+ # PROMPT CREDENTIALS / CONNECT
1190
+ # ------------------------------------------------------------------
1191
+ mcl.add(
1192
+ (
1193
+ r'^\s*PROMPT(?:\s+MESSAGE\s+"(?P<message>(.|\n)*)")?\s+CREDENTIALS\s+(?P<user>\w+)\s+(?P<pw>\w+)\s*$',
1194
+ r'^\s*PROMPT(?:\s+"(?P<message>(.|\n)*)")?\s+CREDENTIALS\s+(?P<user>\w+)\s+(?P<pw>\w+)\s*$',
1195
+ ),
1196
+ x_prompt_credentials,
1197
+ description="PROMPT CREDENTIALS",
1198
+ category="prompt",
1199
+ )
1200
+ mcl.add(
1201
+ (
1202
+ r'^\s*PROMPT(?:\s+MESSAGE\s+"(?P<message>(.|\n)*)")?\s+CONNECT\s+AS\s+(?P<alias>\w+)(?:\s+HELP\s+(?P<help>[^\s]+))?\s*$',
1203
+ r'^\s*PROMPT(?:\s+MESSAGE\s+"(?P<message>(.|\n)*)")?\s+CONNECT\s+AS\s+(?P<alias>\w+)(?:\s+HELP\s+"(?P<help>[^"]+)")?\s*$',
1204
+ r'^\s*CONNECT\s+PROMPT(?:\s+MESSAGE\s+"(?P<message>(.|\n)*)")?\s+AS\s+(?P<alias>\w+)(?:\s+HELP\s+(?P<help>[^\s]+))?\s*$',
1205
+ r'^\s*CONNECT\s+PROMPT(?:\s+MESSAGE\s+"(?P<message>(.|\n)*)")?\s+AS\s+(?P<alias>\w+)(?:\s+HELP\s+"(?P<help>[^"]+)")?\s*$',
1206
+ r'^\s*PROMPT(?:\s+"(?P<message>(.|\n)*)")?\s+CONNECT\s+AS\s+(?P<alias>\w+)(?:\s+HELP\s+(?P<help>[^\s]+))?\s*$',
1207
+ r'^\s*PROMPT(?:\s+"(?P<message>(.|\n)*)")?\s+CONNECT\s+AS\s+(?P<alias>\w+)(?:\s+HELP\s+"(?P<help>[^"]+)")?\s*$',
1208
+ r'^\s*CONNECT\s+PROMPT(?:\s+"(?P<message>(.|\n)*)")?\s+AS\s+(?P<alias>\w+)(?:\s+HELP\s+(?P<help>[^\s]+))?\s*$',
1209
+ r'^\s*CONNECT\s+PROMPT(?:\s+"(?P<message>(.|\n)*)")?\s+AS\s+(?P<alias>\w+)(?:\s+HELP\s+"(?P<help>[^"]+)")?\s*$',
1210
+ ),
1211
+ x_prompt_connect,
1212
+ description="PROMPT CONNECT",
1213
+ category="prompt",
1214
+ )
1215
+
1216
+ # ------------------------------------------------------------------
1217
+ # TIMER / LOG / SUB_INI
1218
+ # ------------------------------------------------------------------
1219
+ mcl.add(r"^\s*TIMER\s+(?P<onoff>ON|OFF)\s*$", x_timer, description="TIMER", category="config")
1220
+ mcl.add(r'^\s*LOG\s+"(?P<message>.+)"\s*$', x_log, description="LOG", category="action")
1221
+ mcl.add(
1222
+ ins_fn_rxs(r"^\s*SUB_INI\s+(?:FILE\s+)?", r"(?:\s+SECTION)?\s+(?P<section>\w+)\s*$"),
1223
+ x_sub_ini,
1224
+ description="SUB_INI",
1225
+ category="action",
1226
+ )
1227
+
1228
+ # ------------------------------------------------------------------
1229
+ # CONSOLE
1230
+ # ------------------------------------------------------------------
1231
+ mcl.add(
1232
+ r"^\s*CONSOLE\s+(?P<hideshow>HIDE|SHOW)\s*$",
1233
+ x_console_hideshow,
1234
+ description="CONSOLE",
1235
+ category="prompt",
1236
+ )
1237
+ mcl.add(r"^\s*CONSOLE\s+WIDTH\s+(?P<width>\d+)\s*$", x_consolewidth)
1238
+ mcl.add(r"^\s*CONSOLE\s+HEIGHT\s+(?P<height>\d+)\s*$", x_consoleheight)
1239
+ mcl.add(r'^\s*CONSOLE\s+STATUS\s+"(?P<message>.*)"\s*$', x_consolestatus)
1240
+ mcl.add(
1241
+ ins_fn_rxs(r"^\s*CONSOLE\s+SAVE(?:\s+(?P<append>APPEND))?\s+TO\s+", r"\s*$"),
1242
+ x_consolesave,
1243
+ )
1244
+ mcl.add(r'^\s*CONSOLE\s+WAIT(?:\s+"(?P<message>.+)")?\s*$', x_consolewait)
1245
+ mcl.add(
1246
+ r"^\s*CONSOLE\s+PROGRESS\s+(?P<num>[0-9]+(?:\.[0-9]+)?)(?:\s*/\s*(?P<total>[0-9]+(?:\.[0-9]+)?))?\s*$",
1247
+ x_consoleprogress,
1248
+ )
1249
+ mcl.add(r"^\s*CONSOLE\s+(?P<onoff>ON|OFF)\s*$", x_console)
1250
+
1251
+ # ------------------------------------------------------------------
1252
+ # DISCONNECT / AUTOCOMMIT
1253
+ # ------------------------------------------------------------------
1254
+ mcl.add(
1255
+ r"^\s*DISCONNECT(?:(?:\s+FROM)?\s+(?P<alias>[A-Z][A-Z0-9_]*))?\s*$",
1256
+ x_disconnect,
1257
+ description="DISCONNECT",
1258
+ category="action",
1259
+ )
1260
+ mcl.add(
1261
+ r"^\s*AUTOCOMMIT\s+OFF\s*$",
1262
+ x_autocommit_off,
1263
+ description="AUTOCOMMIT",
1264
+ category="action",
1265
+ )
1266
+ mcl.add(r"^\s*AUTOCOMMIT\s+ON(?:\s+WITH\s+(?P<action>COMMIT|ROLLBACK))?\s*$", x_autocommit_on)
1267
+
1268
+ # ------------------------------------------------------------------
1269
+ # WRITE SCRIPT / MAX_INT / PG_VACUUM
1270
+ # ------------------------------------------------------------------
1271
+ mcl.add(
1272
+ ins_fn_rxs(
1273
+ r"^\s*(?:DEBUG\s+)?WRITE\s+SCRIPT\s+(?P<script_id>\w+)(?:\s+(?P<append>APPEND\s+)?TO\s+",
1274
+ r")?\s*$",
1275
+ ),
1276
+ x_writescript,
1277
+ description="WRITE SCRIPT",
1278
+ category="action",
1279
+ )
1280
+ mcl.add(r"^\s*MAX_INT\s+(?P<maxint>[0-9]+)\s*$", x_max_int, description="MAX_INT", category="action")
1281
+ mcl.add(r"^\s*PG_VACUUM(?P<vacuum_args>.*)\s*$", x_pg_vacuum, description="PG_VACUUM", category="action")
1282
+
1283
+ # ------------------------------------------------------------------
1284
+ # ZIP
1285
+ # ------------------------------------------------------------------
1286
+ mcl.add(
1287
+ ins_fn_rxs(
1288
+ r"^\s*ZIP\s+(?P<filename>[^ ]+)(?:\s+(?P<append>APPEND))?\s+TO\s+ZIPFILE\s+",
1289
+ r"\s*$",
1290
+ symbolicname="zipfilename",
1291
+ ),
1292
+ x_zip,
1293
+ description="ZIP",
1294
+ category="action",
1295
+ )
1296
+ mcl.add(
1297
+ ins_fn_rxs(
1298
+ r'^\s*ZIP\s+"(?P<filename>[^"]+)"(?:\s+(?P<append>APPEND))?\s+TO\s+ZIPFILE\s+',
1299
+ r"\s*$",
1300
+ symbolicname="zipfilename",
1301
+ ),
1302
+ x_zip,
1303
+ )
1304
+
1305
+ # ------------------------------------------------------------------
1306
+ # HALT (various forms)
1307
+ # ------------------------------------------------------------------
1308
+ mcl.add(
1309
+ ins_fn_rxs(
1310
+ r'^\s*HALT\s*(?:\s+MESSAGE)?(?:\s+"(?P<errmsg>.+)"(?:\s+(?P<tee>TEE\s+TO\s+',
1311
+ r"))?)?(?:\s+EXIT_STATUS\s+(?P<errorlevel>\d+))?\s*$",
1312
+ ),
1313
+ x_halt,
1314
+ description="HALT",
1315
+ category="control",
1316
+ )
1317
+ for errmsg_delim in (r"\[", r"\#", r"\`", r"\'", r"\~", r'"'):
1318
+ # Use the same open/close bracket pair for the errmsg capture
1319
+ open_c = errmsg_delim if not errmsg_delim.startswith("\\") else errmsg_delim[1:]
1320
+ close_c = "]" if open_c == "[" else open_c
1321
+ mcl.add(
1322
+ ins_table_rxs(
1323
+ ins_fn_rxs(
1324
+ rf"^\s*HALT(?:\s+MESSAGE)?\s+{errmsg_delim}(?P<errmsg>(.|\n)*){re.escape(close_c)}"
1325
+ r"(?:\s+(?P<tee>TEE\s+TO\s+",
1326
+ r"))?(?:\s+DISPLAY\s+",
1327
+ ),
1328
+ r")?(?:\s+EXIT_STATUS\s+(?P<errorlevel>\d+))?\s*$",
1329
+ ),
1330
+ x_halt_msg,
1331
+ )
1332
+
1333
+ # ------------------------------------------------------------------
1334
+ # BEGIN / END BATCH / ROLLBACK
1335
+ # ------------------------------------------------------------------
1336
+ mcl.add(r"^\s*BEGIN\s+BATCH\s*$", x_begin_batch, description="BEGIN BATCH", category="block")
1337
+ mcl.add(r"^\s*END\s+BATCH\s*$", x_end_batch, "END BATCH", run_in_batch=True, category="block")
1338
+ mcl.add(r"^\s*ROLLBACK(:?\s+BATCH)?\s*$", x_rollback, "ROLLBACK BATCH", run_in_batch=True, category="block")
1339
+
1340
+ # ------------------------------------------------------------------
1341
+ # ERROR_HALT / METACOMMAND_ERROR_HALT / CANCEL_HALT
1342
+ # ------------------------------------------------------------------
1343
+ mcl.add(
1344
+ r"\s*ERROR_HALT\s+(?P<onoff>ON|OFF|YES|NO|TRUE|FALSE)\s*$",
1345
+ x_error_halt,
1346
+ description="ERROR_HALT",
1347
+ category="control",
1348
+ )
1349
+ mcl.add(
1350
+ r"\s*METACOMMAND_ERROR_HALT\s+(?P<onoff>ON|OFF|YES|NO|TRUE|FALSE)\s*$",
1351
+ x_metacommand_error_halt,
1352
+ set_error_flag=False,
1353
+ description="METACOMMAND_ERROR_HALT",
1354
+ category="control",
1355
+ )
1356
+ mcl.add(
1357
+ r"^\s*CANCEL_HALT\s+(?P<onoff>ON|OFF|YES|NO|TRUE|FALSE)\s*$",
1358
+ x_cancel_halt,
1359
+ description="CANCEL_HALT",
1360
+ category="control",
1361
+ )
1362
+
1363
+ # ------------------------------------------------------------------
1364
+ # LOOP
1365
+ # ------------------------------------------------------------------
1366
+ mcl.add(
1367
+ r"^\s*LOOP\s+(?P<looptype>WHILE|UNTIL)\s*\(\s*(?P<loopcond>.+)\s*\)\s*$",
1368
+ x_loop,
1369
+ description="LOOP",
1370
+ category="control",
1371
+ )
1372
+
1373
+ # ------------------------------------------------------------------
1374
+ # PAUSE
1375
+ # ------------------------------------------------------------------
1376
+ mcl.add(
1377
+ (
1378
+ r'^\s*PAUSE\s+"(?P<text>.+)"(?:\s+(?P<action>HALT|CONTINUE)\s+AFTER\s+(?P<countdown>\d+(?:\.\d*)?)\s+(?P<timeunit>SECONDS|MINUTES))?\s*$',
1379
+ r"^\s*PAUSE\s+'(?P<text>.+)'(?:\s+(?P<action>HALT|CONTINUE)\s+AFTER\s+(?P<countdown>\d+(?:\.\d*)?)\s+(?P<timeunit>SECONDS|MINUTES))?\s*$",
1380
+ r"^\s*PAUSE\s+\[(?P<text>.+)\](?:\s+(?P<action>HALT|CONTINUE)\s+AFTER\s+(?P<countdown>\d+(?:\.\d*)?)\s+(?P<timeunit>SECONDS|MINUTES))?\s*$",
1381
+ ),
1382
+ x_pause,
1383
+ description="PAUSE",
1384
+ category="control",
1385
+ )
1386
+
1387
+ # ------------------------------------------------------------------
1388
+ # PROMPT ENTER_SUB
1389
+ # ------------------------------------------------------------------
1390
+ mcl.add(
1391
+ ins_table_rxs(
1392
+ r'^\s*PROMPT\s+ENTER_SUB\s+(?P<match_str>~?\w+)\s+(?:(?P<password>PASSWORD)\s+)?MESSAGE\s+"(?P<message>([^"]|\n)*)"(?:\s+DISPLAY\s+',
1393
+ r")?(?:\s+TYPE\s+(?P<type>INT|FLOAT|BOOL|IDENT))?(?:\s+(?P<case>LCASE|UCASE))?"
1394
+ r'(?:\s+INITIALLY\s+"(?P<initial>[^"]+)")?(?:\s+HELP\s+(?P<help>[^\s]+))?\s*$',
1395
+ ),
1396
+ x_prompt_enter,
1397
+ description="PROMPT ENTER_SUB",
1398
+ category="prompt",
1399
+ )
1400
+ mcl.add(
1401
+ ins_table_rxs(
1402
+ r'^\s*PROMPT\s+ENTER_SUB\s+(?P<match_str>~?\w+)\s+(?:(?P<password>PASSWORD)\s+)?MESSAGE\s+"(?P<message>([^"]|\n)*)"(?:\s+DISPLAY\s+',
1403
+ r")?(?:\s+TYPE\s+(?P<type>INT|FLOAT|BOOL|IDENT))?(?:\s+(?P<case>LCASE|UCASE))?"
1404
+ r'(?:\s+INITIALLY\s+"(?P<initial>[^"]+)")?(?:\s+HELP\s+"(?P<help>[^+]+)")?\s*$',
1405
+ ),
1406
+ x_prompt_enter,
1407
+ )
1408
+
1409
+ # ------------------------------------------------------------------
1410
+ # PROMPT ENTRY_FORM
1411
+ # ------------------------------------------------------------------
1412
+ mcl.add(
1413
+ ins_table_rxs(
1414
+ r"^\s*PROMPT\s+ENTRY_FORM\s+",
1415
+ ins_table_rxs(
1416
+ r'(?:\s+HELP\s+(?P<help>[^\s]+))?\s+MESSAGE\s+"(?P<message>(.|\n)*)"(?:\s+DISPLAY\s+',
1417
+ r")?\s*$",
1418
+ suffix="disp",
1419
+ ),
1420
+ ),
1421
+ x_prompt_entryform,
1422
+ description="PROMPT ENTRY_FORM",
1423
+ category="prompt",
1424
+ )
1425
+ mcl.add(
1426
+ ins_table_rxs(
1427
+ r"^\s*PROMPT\s+ENTRY_FORM\s+",
1428
+ ins_table_rxs(
1429
+ r'(?:\s+HELP\s+"(?P<help>[^"]+)")?\s+MESSAGE\s+"(?P<message>(.|\n)*)"(?:\s+DISPLAY\s+',
1430
+ r")?\s*$",
1431
+ suffix="disp",
1432
+ ),
1433
+ ),
1434
+ x_prompt_entryform,
1435
+ )
1436
+
1437
+ # ------------------------------------------------------------------
1438
+ # PROMPT SELECT_SUB
1439
+ # ------------------------------------------------------------------
1440
+ mcl.add(
1441
+ ins_table_rxs(
1442
+ r"^\s*PROMPT\s+SELECT_SUB\s+",
1443
+ r'\s+MESSAGE\s+"(?P<msg>(.|\n)*)"(?:\s+(?P<cont>CONTINUE))?(?:\s+HELP\s(?P<help>[^\s]+))?\s*$',
1444
+ ),
1445
+ x_prompt_selectsub,
1446
+ description="PROMPT SELECT_SUB",
1447
+ category="prompt",
1448
+ )
1449
+ mcl.add(
1450
+ ins_table_rxs(
1451
+ r"^\s*PROMPT\s+SELECT_SUB\s+",
1452
+ r'\s+MESSAGE\s+"(?P<msg>(.|\n)*)"(?:\s+(?P<cont>CONTINUE))?(?:\s+HELP\s"(?P<help>[^"]+)")?\s*$',
1453
+ ),
1454
+ x_prompt_selectsub,
1455
+ )
1456
+
1457
+ # ------------------------------------------------------------------
1458
+ # PROMPT PAUSE
1459
+ # ------------------------------------------------------------------
1460
+ mcl.add(
1461
+ (
1462
+ r'^\s*PROMPT\s+PAUSE\s+"(?P<text>.+)"(?:\s+(?P<action>HALT|CONTINUE)\s+AFTER\s+(?P<countdown>\d+(?:\.\d*)?)\s+(?P<timeunit>SECONDS|MINUTES))?\s*$',
1463
+ r"^\s*PROMPT\s+PAUSE\s+'(?P<text>.+)'(?:\s+(?P<action>HALT|CONTINUE)\s+AFTER\s+(?P<countdown>\d+(?:\.\d*)?)\s+(?P<timeunit>SECONDS|MINUTES))?\s*$",
1464
+ r"^\s*PROMPT\s+PAUSE\s+\[(?P<text>.+)\](?:\s+(?P<action>HALT|CONTINUE)\s+AFTER\s+(?P<countdown>\d+(?:\.\d*)?)\s+(?P<timeunit>SECONDS|MINUTES))?\s*$",
1465
+ ),
1466
+ x_prompt_pause,
1467
+ description="PROMPT PAUSE",
1468
+ category="prompt",
1469
+ )
1470
+
1471
+ # ------------------------------------------------------------------
1472
+ # ASK
1473
+ # ------------------------------------------------------------------
1474
+ mcl.add(
1475
+ (
1476
+ r'^\s*ASK\s+"(?P<question>.+)"\s+SUB\s+(?P<match>~?\w+)\s*$',
1477
+ r"^\s*ASK\s+'(?P<question>.+)'\s+SUB\s+(?P<match>~?\w+)\s*$",
1478
+ r"^\s*ASK\s+\[(?P<question>.+)\]\s+SUB\s+(?P<match>~?\w+)\s*$",
1479
+ ),
1480
+ x_ask,
1481
+ description="ASK",
1482
+ category="prompt",
1483
+ )
1484
+
1485
+ # ------------------------------------------------------------------
1486
+ # PROMPT COMPARE / PROMPT ASK COMPARE
1487
+ # ------------------------------------------------------------------
1488
+ mcl.add(
1489
+ ins_table_rxs(
1490
+ r"^\s*PROMPT\s+COMPARE\s+",
1491
+ ins_table_rxs(
1492
+ r"(?:\s+IN\s+(?P<alias1>\w+))?\s+(?P<orient>AND|BESIDE)\s+",
1493
+ r'(?:\s+IN\s+(?P<alias2>\w+))?\s+(?:PK|KEY)\s*\((?P<pks>(("[A-Z_0-9]+")|[A-Z_0-9]+)'
1494
+ r'(\s*,\s*(("[A-Z_0-9]+")|[A-Z_0-9]+))*)\)(?:\s+HELP\s+(?P<help>[^\s]+))?\s+MESSAGE\s+"(?P<msg>(.|\n)*)"\s*$',
1495
+ suffix="2",
1496
+ ),
1497
+ suffix="1",
1498
+ ),
1499
+ x_prompt_compare,
1500
+ description="PROMPT COMPARE",
1501
+ category="prompt",
1502
+ )
1503
+ mcl.add(
1504
+ ins_table_rxs(
1505
+ r"^\s*PROMPT\s+COMPARE\s+",
1506
+ ins_table_rxs(
1507
+ r"(?:\s+IN\s+(?P<alias1>\w+))?\s+(?P<orient>AND|BESIDE)\s+",
1508
+ r'(?:\s+IN\s+(?P<alias2>\w+))?\s+(?:PK|KEY)\s*\((?P<pks>(("[A-Z_0-9]+")|[A-Z_0-9]+)'
1509
+ r'(\s*,\s*(("[A-Z_0-9]+")|[A-Z_0-9]+))*)\)(?:\s+HELP\s+"(?P<help>[^"]+)")?\s+MESSAGE\s+"(?P<msg>(.|\n)*)"\s*$',
1510
+ suffix="2",
1511
+ ),
1512
+ suffix="1",
1513
+ ),
1514
+ x_prompt_compare,
1515
+ )
1516
+ mcl.add(
1517
+ ins_table_rxs(
1518
+ r'^\s*PROMPT\s+ASK\s+"(?P<msg>(.|\n)*)"\s+SUB\s+(?P<match>~?\w+)\s+COMPARE\s+',
1519
+ ins_table_rxs(
1520
+ r"(?:\s+IN\s+(?P<alias1>\w+))?\s+(?P<orient>AND|BESIDE)\s+",
1521
+ r'(?:\s+IN\s+(?P<alias2>\w+))?\s+(?:PK|KEY)\s*\((?P<pks>(("[A-Z_0-9]+")|[A-Z_0-9]+)'
1522
+ r'(\s*,\s*(("[A-Z_0-9]+")|[A-Z_0-9]+))*)\)(?:\s+HELP\s+(?P<help>[^\s]+))?\s*$',
1523
+ suffix="2",
1524
+ ),
1525
+ suffix="1",
1526
+ ),
1527
+ x_prompt_ask_compare,
1528
+ description="PROMPT ASK COMPARE",
1529
+ category="prompt",
1530
+ )
1531
+ mcl.add(
1532
+ ins_table_rxs(
1533
+ r'^\s*PROMPT\s+ASK\s+"(?P<msg>(.|\n)*)"\s+SUB\s+(?P<match>~?\w+)\s+COMPARE\s+',
1534
+ ins_table_rxs(
1535
+ r"(?:\s+IN\s+(?P<alias1>\w+))?\s+(?P<orient>AND|BESIDE)\s+",
1536
+ r'(?:\s+IN\s+(?P<alias2>\w+))?\s+(?:PK|KEY)\s*\((?P<pks>(("[A-Z_0-9]+")|[A-Z_0-9]+)'
1537
+ r'(\s*,\s*(("[A-Z_0-9]+")|[A-Z_0-9]+))*)\)(?:\s+HELP\s+"(?P<help>[^"]+)")?\s*$',
1538
+ suffix="2",
1539
+ ),
1540
+ suffix="1",
1541
+ ),
1542
+ x_prompt_ask_compare,
1543
+ )
1544
+
1545
+ # ------------------------------------------------------------------
1546
+ # PROMPT MAP
1547
+ # ------------------------------------------------------------------
1548
+ mcl.add(
1549
+ ins_table_rxs(
1550
+ r'^\s*PROMPT\s+MESSAGE\s+"(?P<message>(.|\n)*)"\s+MAP\s+',
1551
+ r"\s*LAT\s+(?P<lat_col>\w+)\s+LON\s+(?P<lon_col>\w+)"
1552
+ r"(?:\s+LABEL\s+(?P<label_col>\w+))?(?:\s+COLOR\s+(?P<color_col>\w+))?"
1553
+ r"(?:\s+SYMBOL\s+(?P<symbol_col>\w+))?\s*$",
1554
+ ),
1555
+ x_prompt_map,
1556
+ description="PROMPT MAP",
1557
+ category="prompt",
1558
+ )
1559
+ mcl.add(
1560
+ ins_table_rxs(
1561
+ r'^\s*PROMPT\s+MESSAGE\s+"(?P<message>(.|\n)*)"\s+MAP\s+',
1562
+ r'\s*LAT\s+"(?P<lat_col>[\w+ ])"\s+LON\s+"(?P<lon_col>[\w+ ])"'
1563
+ r'(?:\s+LABEL\s+"(?P<label_col>[\w+ ])")?(?:\s+COLOR\s+"(?P<color_col>[\w+ ])")?'
1564
+ r'(?:\s+SYMBOL\s+"(?P<symbol_col>[\w+ ])")?\s*$',
1565
+ ),
1566
+ x_prompt_map,
1567
+ )
1568
+
1569
+ # ------------------------------------------------------------------
1570
+ # EMAIL
1571
+ # ------------------------------------------------------------------
1572
+ mcl.add(
1573
+ ins_fn_rxs(
1574
+ ins_fn_rxs(
1575
+ r"^\s*EMAIL\s+"
1576
+ r"FROM\s+(?P<from>[A-Za-z0-9_\-\.!#$%&\'*+/=?^`{|}~]+@[A-Za-z0-9]+(-[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)*)\s+"
1577
+ r"TO\s+(?P<to>[A-Za-z0-9_\-\.!#$%&\'*+/=?^`{|}~]+@[A-Za-z0-9]+(-[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)*"
1578
+ r"([;,]\s*[A-Za-z0-9\-\.!#$%&\'*+/=?^`{|}~]+@[A-Za-z0-9]+(-[A-Za-z0-9]+)*(\.[A-Za-z0-9]+)*)*)\s+"
1579
+ r'SUBJECT "(?P<subject>[^"]+)"\s+'
1580
+ r'MESSAGE\s+"(?P<msg>[^"]*)"'
1581
+ r"(\s+MESSAGE_FILE\s+",
1582
+ r")?(\s+ATTACH(MEANT)?_FILE\s+",
1583
+ "msg_file",
1584
+ ),
1585
+ r")?\s*$",
1586
+ "att_file",
1587
+ ),
1588
+ x_email,
1589
+ description="EMAIL",
1590
+ category="action",
1591
+ )
1592
+
1593
+ # ------------------------------------------------------------------
1594
+ # EXPORT_METADATA
1595
+ # ------------------------------------------------------------------
1596
+ mcl.add(
1597
+ ins_fn_rxs(
1598
+ ins_fn_rxs(
1599
+ r"^\s*EXPORT_METADATA(?:\s+(?P<append>APPEND))?(?:\s+(?P<all>ALL))?\s+TO\s+",
1600
+ r"(?:\s+IN\s+ZIPFILE\s+",
1601
+ ),
1602
+ rf")?\s+AS\s+(?P<format>{'|'.join(METADATA_FORMATS)})",
1603
+ symbolicname="zipfilename",
1604
+ ),
1605
+ x_export_metadata,
1606
+ description="EXPORT_METADATA",
1607
+ category="action",
1608
+ )
1609
+ mcl.add(
1610
+ ins_table_rxs(
1611
+ r"^\s*EXPORT_METADATA(?:\s+(?P<all>ALL))?\s+INTO(?:\s+(?P<new>NEW|REPLACEMENT))?\s+TABLE\s+",
1612
+ r"\s*$",
1613
+ ),
1614
+ x_export_metadata_table,
1615
+ )
1616
+
1617
+ # ------------------------------------------------------------------
1618
+ # SUB operations
1619
+ # ------------------------------------------------------------------
1620
+ mcl.add(
1621
+ r"^\s*SUB_EMPTY\s+(?P<match>[+~]?\w+)\s*$",
1622
+ x_sub_empty,
1623
+ description="SUB_EMPTY",
1624
+ category="action",
1625
+ )
1626
+ mcl.add(
1627
+ r"^\s*SUB_ADD\s+(?P<match>[+~]?\w+)\s+(?P<increment>[+\-0-9\.*/() ]+)\s*$",
1628
+ x_sub_add,
1629
+ description="SUB_ADD",
1630
+ category="action",
1631
+ )
1632
+ mcl.add(
1633
+ r"^\s*SUB_APPEND\s+(?P<match>[+~]?\w+)\s(?P<repl>(.|\n)*)$",
1634
+ x_sub_append,
1635
+ description="SUB_APPEND",
1636
+ category="action",
1637
+ )
1638
+
1639
+ # ------------------------------------------------------------------
1640
+ # IF / ORIF / ANDIF / ELSEIF / ELSE / ENDIF
1641
+ # ------------------------------------------------------------------
1642
+ mcl.add(
1643
+ r"^\s*ORIF\s*\(\s*(?P<condtest>.+)\s*\)\s*$",
1644
+ x_if_orif,
1645
+ description="ORIF",
1646
+ run_when_false=True,
1647
+ category="control",
1648
+ )
1649
+ mcl.add(
1650
+ r"^\s*ELSEIF\s*\(\s*(?P<condtest>.+)\s*\)\s*$",
1651
+ x_if_elseif,
1652
+ description="ELSEIF",
1653
+ run_when_false=True,
1654
+ category="control",
1655
+ )
1656
+ mcl.add(r"^\s*ANDIF\s*\(\s*(?P<condtest>.+)\s*\)\s*$", x_if_andif, description="ANDIF", category="control")
1657
+ mcl.add(r"^\s*ELSE\s*$", x_if_else, description="ELSE", run_when_false=True, category="control")
1658
+ mcl.add(
1659
+ r"^\s*IF\s*\(\s*(?P<condtest>.+)\s*\)\s*{\s*(?P<condcmd>.+)\s*}\s*$",
1660
+ x_if,
1661
+ description="IF",
1662
+ category="control",
1663
+ )
1664
+ mcl.add(r"^\s*IF\s*\(\s*(?P<condtest>.+)\s*\)\s*$", x_if_block, run_when_false=True, category="control")
1665
+ mcl.add(r"^\s*ENDIF\s*$", x_if_end, description="ENDIF", run_when_false=True, category="control")
1666
+
1667
+ # ------------------------------------------------------------------
1668
+ # CONNECT — SQL Server
1669
+ # ------------------------------------------------------------------
1670
+ mcl.add(
1671
+ (
1672
+ r"^CONNECT\s+USER\s+TO\s+SQLSERVER\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
1673
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
1674
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1675
+ r'^CONNECT\s+USER\s+TO\s+SQLSERVER\s*\(\s*SERVER\s*=\s*"(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)"\s*,\s*'
1676
+ r'DB\s*=\s*"(?P<db_name>[A-Z][A-Z0-9_\- ]*)"(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?'
1677
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1678
+ ),
1679
+ x_connect_user_ssvr,
1680
+ )
1681
+ mcl.add(
1682
+ (
1683
+ r"^CONNECT\s+TO\s+SQLSERVER\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\/\\\-\.]*)\s*,\s*"
1684
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*USER\s*=\s*(?P<user>[A-Z][A-Z0-9_~`!@#$%^&\*\+=\/\?\.-]*)"
1685
+ r"\s*,\s*NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
1686
+ r"(?:\s*,\s+PASSWORD\s*=\s*(?P<password>[^\s\)]+))?"
1687
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1688
+ r'^CONNECT\s+TO\s+SQLSERVER\s*\(\s*SERVER\s*=\s*"(?P<server>[A-Z0-9][A-Z0-9_\/\\\s\-\.]*)"\s*,\s*'
1689
+ r'DB\s*=\s*"(?P<db_name>[A-Z][A-Z0-9_\-\s]*)"(?:\s*,\s*USER\s*=\s*(?P<user>[A-Z][A-Z0-9_~`!@#$%^&\*\+=\/\?\.-]*)'
1690
+ r"\s*,\s*NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
1691
+ r"(?:\s*,\s+PASSWORD\s*=\s*(?P<password>[^\s\)]+))?"
1692
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1693
+ ),
1694
+ x_connect_ssvr,
1695
+ )
1696
+
1697
+ # ------------------------------------------------------------------
1698
+ # COPY QUERY / COPY
1699
+ # ------------------------------------------------------------------
1700
+ mcl.add(
1701
+ ins_table_rxs(
1702
+ r"^COPY QUERY\s+<<\s*(?P<query>.*;)\s*>>\s+FROM\s+(?P<alias1>[A-Z][A-Z0-9_]*)\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?",
1703
+ r" IN\s+(?P<alias2>[A-Z][A-Z0-9_]*)\s*$",
1704
+ ),
1705
+ x_copy_query,
1706
+ description="COPY QUERY",
1707
+ category="action",
1708
+ )
1709
+ mcl.add(
1710
+ (
1711
+ r"^COPY\s+(?:(?P<schema1>[A-Z][A-Z0-9_\-\/\:]*)\.)?(?P<table1>[A-Z][A-Z0-9_\-\/\:]*)\s+FROM\s+(?P<alias1>[A-Z][A-Z0-9_]*)\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?(?:(?P<schema2>[A-Z][A-Z0-9_\-\/\:]*)\.)?(?P<table2>[A-Z][A-Z0-9_\-\/\:]*)\s+IN\s+(?P<alias2>[A-Z][A-Z0-9_]*)\s*$",
1712
+ r'^COPY\s+(?:"(?P<schema1>[A-Z][A-Z0-9_\-\/\: ]*)"\.)?"(?P<table1>[A-Z][A-Z0-9_\-\/\: ]*)"\s+FROM\s+(?P<alias1>[A-Z][A-Z0-9_]*)\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?(?:"(?P<schema2>[A-Z][A-Z0-9_\-\/\:]*)"\.)?"(?P<table2>[A-Z][A-Z0-9_\-\/\:]*)"\s+IN\s+(?P<alias2>[A-Z][A-Z0-9_]*)\s*$',
1713
+ r'^COPY\s+(?:(?P<schema1>[A-Z][A-Z0-9_\-\/\:]*)\.)?(?P<table1>[A-Z][A-Z0-9_\-\/\:]*)\s+FROM\s+(?P<alias1>[A-Z][A-Z0-9_]*)\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?(?:"(?P<schema2>[A-Z][A-Z0-9_\-\/\:]*)"\.)?"(?P<table2>[A-Z][A-Z0-9_\-\/\:]*)"\s+IN\s+(?P<alias2>[A-Z][A-Z0-9_]*)\s*$',
1714
+ r'^COPY\s+(?:"(?P<schema1>[A-Z][A-Z0-9_\-\/\: ]*)"\.)?"(?P<table1>[A-Z][A-Z0-9_\-\/\: ]*)"\s+FROM\s+(?P<alias1>[A-Z][A-Z0-9_]*)\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?(?:(?P<schema2>[A-Z][A-Z0-9_\-\/\:]*)\.)?(?P<table2>[A-Z][A-Z0-9_\-\/\:]*)\s+IN\s+(?P<alias2>[A-Z][A-Z0-9_]*)\s*$',
1715
+ r"^COPY\s+(?:\[(?P<schema1>[A-Z][A-Z0-9_\-\/\: ]*)\]\.)?\[(?P<table1>[A-Z][A-Z0-9_\-\/\: ]*)\]\s+FROM\s+(?P<alias1>[A-Z][A-Z0-9_]*)\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?(?:\[(?P<schema2>[A-Z][A-Z0-9_\-\/\:]*)\]\.)?\[(?P<table2>[A-Z][A-Z0-9_\-\/\:]*)\]\s+IN\s+(?P<alias2>[A-Z][A-Z0-9_]*)\s*$",
1716
+ ),
1717
+ x_copy,
1718
+ description="COPY",
1719
+ category="action",
1720
+ )
1721
+
1722
+ # ------------------------------------------------------------------
1723
+ # APPEND/EXTEND SCRIPT
1724
+ # ------------------------------------------------------------------
1725
+ mcl.add(
1726
+ r"\s*APPEND\s+SCRIPT\s+(?P<script1>\w+)\s+TO\s+(?P<script2>\w+)\s*$",
1727
+ x_extendscript,
1728
+ description="APPEND SCRIPT",
1729
+ category="action",
1730
+ )
1731
+ mcl.add(
1732
+ r"\s*EXTEND\s+SCRIPT\s+(?P<script>\w+)\s+WITH\s+METACOMMAND\s+(?P<cmd>.+)\s*$",
1733
+ x_extendscript_metacommand,
1734
+ description="EXTEND SCRIPT",
1735
+ category="action",
1736
+ )
1737
+ mcl.add(
1738
+ r"\s*EXTEND\s+SCRIPT\s+(?P<script>\w+)\s+WITH\s+SQL\s+(?P<sql>.+;)\s*$",
1739
+ x_extendscript_sql,
1740
+ )
1741
+
1742
+ # ------------------------------------------------------------------
1743
+ # CONNECT — DuckDB / SQLite / PostgreSQL / MySQL
1744
+ # ------------------------------------------------------------------
1745
+ mcl.add(
1746
+ ins_fn_rxs(
1747
+ r"^CONNECT\s+TO\s+DUCKDB\s*\(\s*FILE\s*=\s*",
1748
+ r"(?:\s*,\s*(?P<new>NEW))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1749
+ ),
1750
+ x_connect_duckdb,
1751
+ )
1752
+ mcl.add(
1753
+ ins_fn_rxs(
1754
+ r"^CONNECT\s+TO\s+SQLITE\s*\(\s*FILE\s*=\s*",
1755
+ r"(?:\s*,\s*(?P<new>NEW))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1756
+ ),
1757
+ x_connect_sqlite,
1758
+ )
1759
+ mcl.add(
1760
+ (
1761
+ r"^CONNECT\s+USER\s+TO\s+POSTGRESQL\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
1762
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
1763
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1764
+ r'^CONNECT\s+USER\s+TO\s+POSTGRESQL\s*\(\s*SERVER\s*=\s*"(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)"\s*,\s*'
1765
+ r'DB\s*=\s*"(?P<db_name>[A-Z][A-Z0-9_\-]*)"(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?'
1766
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1767
+ ),
1768
+ x_connect_user_pg,
1769
+ )
1770
+ mcl.add(
1771
+ (
1772
+ r"^CONNECT\s+TO\s+POSTGRESQL\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
1773
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*USER\s*=\s*(?P<user>[A-Z][A-Z0-9_\-@\.]*)\s*,\s*"
1774
+ r"NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
1775
+ r"(?:\s*,\s+PASSWORD\s*=\s*(?P<password>[^\s\)]+))?"
1776
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?(?:\s*,\s*(?P<new>NEW))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1777
+ r'^CONNECT\s+TO\s+POSTGRESQL\s*\(\s*SERVER\s*=\s*"(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)"\s*,\s*'
1778
+ r'DB\s*=\s*"(?P<db_name>[A-Z][A-Z0-9_\-]*)"(?:\s*,\s*USER\s*=\s*"(?P<user>[A-Z][A-Z0-9_\-@\.]*)"\s*,\s*'
1779
+ r"NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
1780
+ r'(?:\s*,\s+PASSWORD\s*=\s*"(?P<password>[^\s\)]+)")?'
1781
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?(?:\s*,\s*(?P<new>NEW))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1782
+ ),
1783
+ x_connect_pg,
1784
+ )
1785
+ mcl.add(
1786
+ (
1787
+ r"^CONNECT\s+USER\s+TO\s+MYSQL\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
1788
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
1789
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1790
+ r'^CONNECT\s+USER\s+TO\s+MYSQL\s*\(\s*SERVER\s*=\s*"(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)"\s*,\s*'
1791
+ r'DB\s*=\s*"(?P<db_name>[A-Z][A-Z0-9_\-]*)"(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?'
1792
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1793
+ r"^CONNECT\s+USER\s+TO\s+MARIADB\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
1794
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
1795
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1796
+ r'^CONNECT\s+USER\s+TO\s+MARIADB\s*\(\s*SERVER\s*=\s*"(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)"\s*,\s*'
1797
+ r'DB\s*=\s*"(?P<db_name>[A-Z][A-Z0-9_\-]*)"(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?'
1798
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1799
+ ),
1800
+ x_connect_user_mysql,
1801
+ )
1802
+ mcl.add(
1803
+ (
1804
+ r"^CONNECT\s+TO\s+MYSQL\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
1805
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*USER\s*=\s*(?P<user>[A-Z][A-Z0-9_@\-\.]*)\s*,\s*"
1806
+ r"NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
1807
+ r"(?:\s*,\s+PASSWORD\s*=\s*(?P<password>[^\s]+))?"
1808
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1809
+ r"^CONNECT\s+TO\s+MARIADB\s*\(\s*SERVER\s*=\s*(?P<server>[A-Z0-9][A-Z0-9_\-\.]*)\s*,\s*"
1810
+ r"DB\s*=\s*(?P<db_name>[A-Z][A-Z0-9_\-]*)(?:\s*,\s*USER\s*=\s*(?P<user>[A-Z][A-Z0-9_&\-\.]*)\s*,\s*"
1811
+ r"NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?(?:\s*,\s*PORT\s*=\s*(?P<port>\d+))?"
1812
+ r"(?:\s*,\s+PASSWORD\s*=\s*(?P<password>[^\s]+))?"
1813
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1814
+ ),
1815
+ x_connect_mysql,
1816
+ )
1817
+
1818
+ # ------------------------------------------------------------------
1819
+ # CONNECT DSN
1820
+ # ------------------------------------------------------------------
1821
+ mcl.add(
1822
+ r"^CONNECT\s+TO\s+DSN\s*\(\s*DSN\s*=\s*(?P<dsn>[A-Z0-9][A-Z0-9_\-\.]*)\s*"
1823
+ r"(?:\s*,\s*USER\s*=\s*(?P<user>[A-Z][A-Z0-9_@\-\.]*)\s*,\s*NEED_PWD\s*=\s*(?P<need_pwd>TRUE|FALSE))?"
1824
+ r"(?:\s*,\s+PASSWORD\s*=\s*(?P<password>[^\s\)]+))?"
1825
+ r"(?:\s*,\s*ENCODING\s*=\s*(?P<encoding>[A-Z][A-Z0-9_-]+))?\s*\)\s+AS\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$",
1826
+ x_connect_dsn,
1827
+ )
1828
+
1829
+ # ------------------------------------------------------------------
1830
+ # USE
1831
+ # ------------------------------------------------------------------
1832
+ mcl.add(r"^USE\s+(?P<db_alias>[A-Z][A-Z0-9_]*)\s*$", x_use, description="USE", category="action")
1833
+
1834
+ # ------------------------------------------------------------------
1835
+ # SYSTEM_CMD
1836
+ # ------------------------------------------------------------------
1837
+ mcl.add(
1838
+ r"^\s*SYSTEM_CMD\s*\(\s*(?P<command>.+)\s*\)(?:\s+(?P<continue>CONTINUE))?\s*$",
1839
+ x_system_cmd,
1840
+ description="SYSTEM_CMD",
1841
+ category="action",
1842
+ )
1843
+
1844
+ # ------------------------------------------------------------------
1845
+ # INCLUDE
1846
+ # ------------------------------------------------------------------
1847
+ mcl.add(
1848
+ ins_fn_rxs(r"^\s*INCLUDE(?P<exists>\s+IF\s+EXISTS?)?\s+", r"\s*$"),
1849
+ x_include,
1850
+ description="INCLUDE",
1851
+ category="action",
1852
+ )
1853
+
1854
+ # ------------------------------------------------------------------
1855
+ # IMPORT (CSV / delimited)
1856
+ # ------------------------------------------------------------------
1857
+ mcl.add(
1858
+ ins_table_rxs(
1859
+ r"^\s*IMPORT\s+TO\s+(?:(?P<new>NEW|REPLACEMENT)\s+)?",
1860
+ ins_fn_rxs(
1861
+ r"\s+FROM\s+",
1862
+ r'(?:\s+WITH)?(?:\s+QUOTE\s+(?P<quotechar>NONE|\'|")\s+DELIMITER\s+'
1863
+ r"(?P<delimchar>TAB|UNITSEP|US|,|;|\|))?(?:\s+ENCODING\s+(?P<encoding>\w+))?"
1864
+ r"(?:\s+SKIP\s+(?P<skip>\d+))?\s*$",
1865
+ ),
1866
+ ),
1867
+ x_import,
1868
+ )
1869
+
1870
+ # ------------------------------------------------------------------
1871
+ # RM_FILE / RM_SUB
1872
+ # ------------------------------------------------------------------
1873
+ mcl.add(
1874
+ (
1875
+ r"^RM_FILE\s+(?P<filename>.+)\s*$",
1876
+ r'^RM_FILE\s+"(?P<filename>.+)"\s*$',
1877
+ ),
1878
+ x_rm_file,
1879
+ description="RM_FILE",
1880
+ category="action",
1881
+ )
1882
+ mcl.add(r"^\s*RM_SUB\s+(?P<match>~?\w+)\s*$", x_rm_sub, description="RM_SUB", category="action")
1883
+
1884
+ # ------------------------------------------------------------------
1885
+ # SELECT_SUB / SUBDATA
1886
+ # ------------------------------------------------------------------
1887
+ mcl.add(r"^\s*SELECT_SUB\s+(?P<datasource>.+)\s*$", x_selectsub, description="SELECT_SUB", category="action")
1888
+ mcl.add(
1889
+ r"^\s*SUBDATA\s+(?P<match>[+~]?\w+)\s+(?P<datasource>.+)\s*$",
1890
+ x_subdata,
1891
+ description="SUBDATA",
1892
+ category="action",
1893
+ )
1894
+
1895
+ # ------------------------------------------------------------------
1896
+ # PROMPT ASK (simple)
1897
+ # ------------------------------------------------------------------
1898
+ mcl.add(
1899
+ ins_table_rxs(
1900
+ r'^\s*PROMPT\s+ASK\s+"(?P<question>[^"]+)"\s+SUB\s+(?P<match>~?\w+)(?:\s+DISPLAY\s+',
1901
+ r')?(?:\s+HELP\s+"(?P<help>[^"]+)")?\s*$',
1902
+ ),
1903
+ x_prompt_ask,
1904
+ description="PROMPT ASK",
1905
+ category="prompt",
1906
+ )
1907
+
1908
+ # ------------------------------------------------------------------
1909
+ # PROMPT DISPLAY (table viewer)
1910
+ # ------------------------------------------------------------------
1911
+ mcl.add(
1912
+ ins_table_rxs(
1913
+ r'^\s*PROMPT\s+MESSAGE\s+"(?P<message>(.|\n)*)"\s+DISPLAY\s+',
1914
+ r"(?:\s+HELP\s+(?P<help>[^\s]+))?(?:\s+(?P<free>FREE))?\s*$",
1915
+ ),
1916
+ x_prompt,
1917
+ description="PROMPT DISPLAY",
1918
+ category="prompt",
1919
+ )
1920
+ mcl.add(
1921
+ ins_table_rxs(
1922
+ r'^\s*PROMPT\s+MESSAGE\s+"(?P<message>(.|\n)*)"\s+DISPLAY\s+',
1923
+ r'(?:\s+HELP\s+"(?P<help>[^"]+)")?(?:\s+(?P<free>FREE))?\s*$',
1924
+ ),
1925
+ x_prompt,
1926
+ )
1927
+ mcl.add(
1928
+ ins_table_rxs(
1929
+ r"^\s*PROMPT\s+DISPLAY\s+",
1930
+ r'\s+MESSAGE\s+"(?P<message>(.|\n)*)"(?:\s+HELP\s+(?P<help>[^\s]+))?(?:\s+(?P<free>FREE))?\s*$',
1931
+ ),
1932
+ x_prompt,
1933
+ )
1934
+ mcl.add(
1935
+ ins_table_rxs(
1936
+ r"^\s*PROMPT\s+DISPLAY\s+",
1937
+ r'\s+MESSAGE\s+"(?P<message>(.|\n)*)"(?:\s+HELP\s+"(?P<help>[^"]+)")?(?:\s+(?P<free>FREE))?\s*$',
1938
+ ),
1939
+ x_prompt,
1940
+ )
1941
+
1942
+ # ------------------------------------------------------------------
1943
+ # PROMPT MESSAGE (simple message / MSG)
1944
+ # ------------------------------------------------------------------
1945
+ mcl.add(
1946
+ r'^\s*PROMPT(?:\s+MESSAGE)?\s+"(?P<message>(.|\n)*)"\s*$',
1947
+ x_msg,
1948
+ description="PROMPT MESSAGE",
1949
+ category="prompt",
1950
+ )
1951
+
1952
+ # ------------------------------------------------------------------
1953
+ # WRITE
1954
+ # ------------------------------------------------------------------
1955
+ mcl.add(
1956
+ ins_fn_rxs(
1957
+ r"^\s*WRITE\s+\~(?P<text>([^\~]|\n)*)\~(?:(?:\s+(?P<tee>TEE))?\s+TO\s+",
1958
+ r")?\s*$",
1959
+ ),
1960
+ x_write,
1961
+ description="WRITE",
1962
+ category="action",
1963
+ )
1964
+ mcl.add(
1965
+ ins_fn_rxs(
1966
+ r"^\s*WRITE\s+\#(?P<text>([^\#]|\n)*)\#(?:(?:\s+(?P<tee>TEE))?\s+TO\s+",
1967
+ r")?\s*$",
1968
+ ),
1969
+ x_write,
1970
+ )
1971
+ mcl.add(
1972
+ ins_fn_rxs(
1973
+ r"^\s*WRITE\s+\`(?P<text>([^\`]|\n)*)\`(?:(?:\s+(?P<tee>TEE))?\s+TO\s+",
1974
+ r")?\s*$",
1975
+ ),
1976
+ x_write,
1977
+ )
1978
+ mcl.add(
1979
+ ins_fn_rxs(
1980
+ r"^\s*WRITE\s+\[(?P<text>([^\]]|\n)*)\](?:(?:\s+(?P<tee>TEE))?\s+TO\s+",
1981
+ r")?\s*$",
1982
+ ),
1983
+ x_write,
1984
+ )
1985
+ mcl.add(
1986
+ ins_fn_rxs(
1987
+ r"^\s*WRITE\s+\'(?P<text>([^\']|\n)*)\'(?:(?:\s+(?P<tee>TEE))?\s+TO\s+",
1988
+ r")?\s*$",
1989
+ ),
1990
+ x_write,
1991
+ )
1992
+ mcl.add(
1993
+ ins_fn_rxs(
1994
+ r'^\s*WRITE\s+"(?P<text>([^"]|\n)*)"(?:(?:\s+(?P<tee>TEE))?\s+TO\s+',
1995
+ r")?\s*$",
1996
+ ),
1997
+ x_write,
1998
+ )
1999
+
2000
+ # ------------------------------------------------------------------
2001
+ # SUB (top-level variable assignment — kept near end so more specific
2002
+ # SUB_* patterns above take precedence)
2003
+ # ------------------------------------------------------------------
2004
+ mcl.add(
2005
+ r"^\s*SUB\s+(?P<match>[+~]?\w+)\s+(?P<repl>.+)$",
2006
+ x_sub,
2007
+ description="SUB",
2008
+ category="action",
2009
+ )
2010
+
2011
+ return mcl