kaqing 2.0.186__py3-none-any.whl → 2.0.188__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.
Potentially problematic release.
This version of kaqing might be problematic. Click here for more details.
- adam/app_session.py +1 -1
- adam/embedded_params.py +1 -1
- adam/sql/lark_completer.py +9 -5
- adam/version.py +1 -1
- {kaqing-2.0.186.dist-info → kaqing-2.0.188.dist-info}/METADATA +1 -1
- {kaqing-2.0.186.dist-info → kaqing-2.0.188.dist-info}/RECORD +12 -9
- kaqing-2.0.188.dist-info/top_level.txt +2 -0
- teddy/__init__.py +0 -0
- teddy/lark_parser.py +436 -0
- teddy/lark_parser2.py +618 -0
- kaqing-2.0.186.dist-info/top_level.txt +0 -1
- {kaqing-2.0.186.dist-info → kaqing-2.0.188.dist-info}/WHEEL +0 -0
- {kaqing-2.0.186.dist-info → kaqing-2.0.188.dist-info}/entry_points.txt +0 -0
adam/app_session.py
CHANGED
|
@@ -8,7 +8,7 @@ from adam.log import Log
|
|
|
8
8
|
from adam.sso.idp import Idp
|
|
9
9
|
from adam.sso.idp_login import IdpLogin
|
|
10
10
|
from adam.config import Config
|
|
11
|
-
from adam.utils import debug, debug_trace, json_to_csv, tabulize,
|
|
11
|
+
from adam.utils import debug, debug_trace, json_to_csv, tabulize, log2, log_exc
|
|
12
12
|
from adam.apps import Apps
|
|
13
13
|
|
|
14
14
|
class AppLogin:
|
adam/embedded_params.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
def config():
|
|
2
|
-
return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'container-name': 'c3-server', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'audit': {'endpoint': 'https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/', 'workers': 3, 'timeout': 10, 'log-audit-queries': False, 'athena': {'auto-repair': {'elapsed_hours': 12}, 'region': 'us-west-2', 'catalog': 'AwsDataCatalog', 'database': 'audit', 'repair-partition-tables': 'audit', 'output': 's3://s3.ops--audit/ddl/results', 'repair-cluster-tables': 'cluster'}, 'queries': {'last10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY ts DESC LIMIT {limit}", 'slow10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY CAST(duration AS REAL) DESC LIMIT {limit}", 'top10': "SELECT min(c) AS cluster, line, COUNT(*) AS cnt, avg(CAST(duration AS REAL)) AS duration\nFROM audit WHERE drive <> 'z' and ({date_condition})\nGROUP BY line ORDER BY cnt DESC LIMIT {limit}"}}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}, 'alter-tables': {'excludes': 'system_auth,system_traces,reaper_db,system_distributed,system_views,system,system_schema,system_virtual_schema', 'gc-grace-periods': '3600,86400,864000,7776000', 'batching': True}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'download': {'workers': 8}, 'export': {'workers': 8, 'csv_dir': '/c3/cassandra/tmp', 'column_counts_query': 'select id, count(id) as columns from {table} group by id order by columns desc limit 10', 'default-importer': 'sqlite', 'sqlite': {'workers': 8, 'columns': '<row-key>', 'local-db-dir': '/tmp/qing-db'}, 'athena': {'workers': 8, 'columns': '<keys>', 'bucket': 'c3.ops--qing'}, 'csv': {'workers': 8, 'columns': '<row-key>'}, 'log-prefix': '/tmp/qing'}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': 'https://c3energy.okta.com/oauth2/v1/authorize?response_type=id_token&response_mode=form_post&client_id={client_id}&scope=openid+email+profile+groups&redirect_uri=https%3A%2F%2F{host}%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'local-tmp-dir': '/tmp/qing-db', 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'log-prefix': '/tmp/qing', 'nodetool': {'workers': 96, 'commands_in_line': 40, 'status': {'workers': 32, 'samples': 3, 'commands_in_line': 40}}, 'pg': {'name-pattern': '^{namespace}.*-k8spg-.*', 'excludes': '.helm., -admin-secret', 'agent': {'name': 'ops-pg-agent', 'just-in-time': False, 'timeout': 86400, 'image': 'seanahnsf/kaqing'}, 'default-db': 'postgres', 'default-schema': 'postgres', 'secret': {'endpoint-key': 'postgres-db-endpoint', 'port-key': 'postgres-db-port', 'username-key': 'postgres-admin-username', 'password-key': 'postgres-admin-password'}}, 'pod': {'name': 'ops', 'image': 'seanahnsf/kaqing-cloud', 'sa': {'name': 'ops', 'proto': 'c3', 'additional-cluster-roles': 'c3aiops-k8ssandra-operator'}, 'label-selector': 'run=ops'}, 'preview': {'rows': 10}, 'processes': {'columns': 'pod,cpu-metrics,mem', 'header': 'POD_NAME,M_CPU(USAGE/LIMIT),MEM/LIMIT'}, 'processes-qing': {'columns': 'pod,cpu,mem', 'header': 'POD_NAME,Q_CPU/TOTAL,MEM/LIMIT'}, 'reaper': {'service-name': 'reaper-service', 'port-forward': {'timeout': 86400, 'local-port': 9001}, 'abort-runs-batch': 10, 'show-runs-batch': 100, 'pod': {'cluster-regex': '(.*?-.*?-.*?-.*?)-.*', 'label-selector': 'k8ssandra.io/reaper={cluster}-reaper'}, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-reaper-ui', 'password-item': 'password'}}, 'repair': {'log-path': '/home/cassrepair/logs/', 'image': 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.14', 'secret': 'ciregistryc3iotio', 'env': {'interval': 24, 'timeout': 60, 'pr': False, 'runs': 1}}, 'repl': {'start-drive': 'c', 'a': {'auto-enter': 'c3/c3
|
|
2
|
+
return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'container-name': 'c3-server', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'audit': {'endpoint': 'https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/', 'workers': 3, 'timeout': 10, 'log-audit-queries': False, 'athena': {'auto-repair': {'elapsed_hours': 12}, 'region': 'us-west-2', 'catalog': 'AwsDataCatalog', 'database': 'audit', 'repair-partition-tables': 'audit', 'output': 's3://s3.ops--audit/ddl/results', 'repair-cluster-tables': 'cluster'}, 'queries': {'last10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY ts DESC LIMIT {limit}", 'slow10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY CAST(duration AS REAL) DESC LIMIT {limit}", 'top10': "SELECT min(c) AS cluster, line, COUNT(*) AS cnt, avg(CAST(duration AS REAL)) AS duration\nFROM audit WHERE drive <> 'z' and ({date_condition})\nGROUP BY line ORDER BY cnt DESC LIMIT {limit}"}}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}, 'alter-tables': {'excludes': 'system_auth,system_traces,reaper_db,system_distributed,system_views,system,system_schema,system_virtual_schema', 'gc-grace-periods': '3600,86400,864000,7776000', 'batching': True}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'download': {'workers': 8}, 'export': {'workers': 8, 'csv_dir': '/c3/cassandra/tmp', 'column_counts_query': 'select id, count(id) as columns from {table} group by id order by columns desc limit 10', 'default-importer': 'sqlite', 'sqlite': {'workers': 8, 'columns': '<row-key>', 'local-db-dir': '/tmp/qing-db'}, 'athena': {'workers': 8, 'columns': '<keys>', 'bucket': 'c3.ops--qing'}, 'csv': {'workers': 8, 'columns': '<row-key>'}, 'log-prefix': '/tmp/qing'}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': 'https://c3energy.okta.com/oauth2/v1/authorize?response_type=id_token&response_mode=form_post&client_id={client_id}&scope=openid+email+profile+groups&redirect_uri=https%3A%2F%2F{host}%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'local-tmp-dir': '/tmp/qing-db', 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'log-prefix': '/tmp/qing', 'nodetool': {'workers': 96, 'commands_in_line': 40, 'status': {'workers': 32, 'samples': 3, 'commands_in_line': 40}}, 'pg': {'name-pattern': '^{namespace}.*-k8spg-.*', 'excludes': '.helm., -admin-secret', 'agent': {'name': 'ops-pg-agent', 'just-in-time': False, 'timeout': 86400, 'image': 'seanahnsf/kaqing'}, 'default-db': 'postgres', 'default-schema': 'postgres', 'secret': {'endpoint-key': 'postgres-db-endpoint', 'port-key': 'postgres-db-port', 'username-key': 'postgres-admin-username', 'password-key': 'postgres-admin-password'}}, 'pod': {'name': 'ops', 'image': 'seanahnsf/kaqing-cloud', 'sa': {'name': 'ops', 'proto': 'c3', 'additional-cluster-roles': 'c3aiops-k8ssandra-operator'}, 'label-selector': 'run=ops'}, 'preview': {'rows': 10}, 'processes': {'columns': 'pod,cpu-metrics,mem', 'header': 'POD_NAME,M_CPU(USAGE/LIMIT),MEM/LIMIT'}, 'processes-qing': {'columns': 'pod,cpu,mem', 'header': 'POD_NAME,Q_CPU/TOTAL,MEM/LIMIT'}, 'reaper': {'service-name': 'reaper-service', 'port-forward': {'timeout': 86400, 'local-port': 9001}, 'abort-runs-batch': 10, 'show-runs-batch': 100, 'pod': {'cluster-regex': '(.*?-.*?-.*?-.*?)-.*', 'label-selector': 'k8ssandra.io/reaper={cluster}-reaper'}, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-reaper-ui', 'password-item': 'password'}}, 'repair': {'log-path': '/home/cassrepair/logs/', 'image': 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.14', 'secret': 'ciregistryc3iotio', 'env': {'interval': 24, 'timeout': 60, 'pr': False, 'runs': 1}}, 'repl': {'start-drive': 'c', 'a': {'auto-enter': 'c3/c3'}, 'c': {'auto-enter': 'cluster'}, 'x': {'auto-enter': 'latest'}, 'history': {'push-cat-log-file': True, 'push-cat-remote-log-file': True}, 'background-process': {'auto-nohup': True}}, 'status': {'columns': 'status,address,load,tokens,owns,host_id,gossip,compactions', 'header': '--,Address,Load,Tokens,Owns,Host ID,GOSSIP,COMPACTIONS'}, 'storage': {'columns': 'pod,volume_root,volume_cassandra,snapshots,data,compactions', 'header': 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS'}, 'watch': {'auto': 'rollout', 'timeout': 3600, 'interval': 10}, 'auto-complete': {'c': {'tables': 'lazy'}, 'x': {'tables': 'lazy'}, 'cli': {'cp': 'jit'}, 'export': {'databases': 'jit'}, 'medusa': {'backups': 'jit'}, 'reaper': {'schedules': 'lazy'}}, 'debug': False, 'debugs': {'timings': False, 'exit-on-error': False, 'show-parallelism': False}}
|
adam/sql/lark_completer.py
CHANGED
|
@@ -176,6 +176,8 @@ class LarkCompleter(Completer, AppendableCompleter):
|
|
|
176
176
|
# for lazy completions
|
|
177
177
|
self.nested = NestedCompleter.from_nested_dict(self.options_lambda())
|
|
178
178
|
|
|
179
|
+
nested_words = set()
|
|
180
|
+
|
|
179
181
|
if self.nested:
|
|
180
182
|
# from NestedCompleter
|
|
181
183
|
|
|
@@ -200,6 +202,7 @@ class LarkCompleter(Completer, AppendableCompleter):
|
|
|
200
202
|
)
|
|
201
203
|
|
|
202
204
|
for c in completer.get_completions(new_document, complete_event):
|
|
205
|
+
nested_words.add(c.text)
|
|
203
206
|
yield c
|
|
204
207
|
|
|
205
208
|
# No space in the input: behave exactly like `WordCompleter`.
|
|
@@ -208,6 +211,7 @@ class LarkCompleter(Completer, AppendableCompleter):
|
|
|
208
211
|
list(self.nested.options.keys()), ignore_case=self.nested.ignore_case
|
|
209
212
|
)
|
|
210
213
|
for c in completer.get_completions(document, complete_event):
|
|
214
|
+
nested_words.add(c.text)
|
|
211
215
|
yield c
|
|
212
216
|
|
|
213
217
|
if self.parser:
|
|
@@ -244,12 +248,12 @@ class LarkCompleter(Completer, AppendableCompleter):
|
|
|
244
248
|
def word_matches(word: str) -> bool:
|
|
245
249
|
return word.lower().startswith(word_before_cursor)
|
|
246
250
|
|
|
247
|
-
for
|
|
248
|
-
if word_matches(
|
|
249
|
-
display = self.display_dict.get(
|
|
250
|
-
display_meta = self.meta_dict.get(
|
|
251
|
+
for word in words:
|
|
252
|
+
if word_matches(word) and word not in nested_words:
|
|
253
|
+
display = self.display_dict.get(word, word)
|
|
254
|
+
display_meta = self.meta_dict.get(word, "")
|
|
251
255
|
yield Completion(
|
|
252
|
-
|
|
256
|
+
word,
|
|
253
257
|
-len(word_before_cursor),
|
|
254
258
|
display=display,
|
|
255
259
|
display_meta=display_meta,
|
adam/version.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
adam/__init__.py,sha256=is7iqn2nhRzPymhTEajITEFVyvROZ9GWfNsZX3L5g_o,45
|
|
2
|
-
adam/app_session.py,sha256=
|
|
2
|
+
adam/app_session.py,sha256=I2EX7eNRCP6T9u1BTVLs-5SB1ypyhSwNvVmi4nwoU-0,6836
|
|
3
3
|
adam/apps.py,sha256=f1qsIOYjn4fdpCvMfOBns5YEiA5fMm7yZu3cQrK6z2M,7049
|
|
4
4
|
adam/batch.py,sha256=c1FgH3-glAqBdAXMEGBe9MTHnqGFUXfsgLB1Mw9LV60,24041
|
|
5
5
|
adam/cli.py,sha256=03pIZdomAu7IL-GSP6Eun_PKwwISShRAmfx6eVRPGC0,458
|
|
6
6
|
adam/cli_group.py,sha256=W3zy1BghCtVcEXizq8fBH-93ZRVVwgAyGPzy0sHno1Y,593
|
|
7
7
|
adam/config.py,sha256=snozVzu-jaws8PFMNH489Iv-43ptYa5pp4aGeTKRQnw,2778
|
|
8
8
|
adam/embedded_apps.py,sha256=lKPx63mKzJbNmwz0rgL4gF76M9fDGxraYTtNAIGnZ_s,419
|
|
9
|
-
adam/embedded_params.py,sha256=
|
|
9
|
+
adam/embedded_params.py,sha256=NaD3Oc5LGfT2QRK_S3bOv9O4Ii8kb2JMU1pGxztmW2Q,6427
|
|
10
10
|
adam/log.py,sha256=vcJ1Q8LLnt3NSXqpVcKjAI2OZE6KaD3PEi1kfu_D8qs,1156
|
|
11
11
|
adam/pod_exec_result.py,sha256=85jy_5dS6TEEk5ijU0B62YfUycxmD3dG8mhIHFPipc8,1260
|
|
12
12
|
adam/repl.py,sha256=_GVboHyXRcpr0l0gQAgiQ5LNTXgH3cXSvvC_aNxQmDw,7574
|
|
@@ -20,7 +20,7 @@ adam/utils_issues.py,sha256=nWhzUXmD2IfbT8MzjdyvuvpKrtUoieF74O2joaWFpUU,1438
|
|
|
20
20
|
adam/utils_local.py,sha256=3RIvP5ZluJhOl-z9qi5U4bLFP5_vuYGnjcY-koO40lE,109
|
|
21
21
|
adam/utils_net.py,sha256=byEtNVr8iG9UaD7dM77dN2WEBClB7YNKult7LKFTCOc,428
|
|
22
22
|
adam/utils_sqlite.py,sha256=z34AxQL1HZBcv704NXZsMerWt-BPtnCUUw1rZHQB9Bw,4330
|
|
23
|
-
adam/version.py,sha256=
|
|
23
|
+
adam/version.py,sha256=BINgYFo4SHBKqDKm610CdGiykSH7P1Tcc7tYM9JtmmY,140
|
|
24
24
|
adam/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
adam/checks/check.py,sha256=Qopr3huYcMu2bzQgb99dEUYjFzkjKHRI76S6KA9b9Rk,702
|
|
26
26
|
adam/checks/check_context.py,sha256=FEHkQ32jY1EDopQ2uYWqy9v7aEEX1orLpJWhopwAlh4,402
|
|
@@ -205,7 +205,7 @@ adam/commands/show/show_params.py,sha256=6rM9IgHusa5MrqLdHOBzBMOh1mOnlkFDbZeFGdX
|
|
|
205
205
|
adam/commands/show/show_processes.py,sha256=bkdFF8fvnNCZ1YTe1ucxtKDTt40w1lLOHbx8FcshyEI,2378
|
|
206
206
|
adam/commands/show/show_storage.py,sha256=AfqOn9ypo4G762aN8a5ZWW4IUpPDehM6R3QByGCpMFk,1734
|
|
207
207
|
adam/sql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
208
|
-
adam/sql/lark_completer.py,sha256=
|
|
208
|
+
adam/sql/lark_completer.py,sha256=RrL4OK_3pmYXPMxWO29xikSlt1v1Z3bAWsTv9e9Wu_c,12480
|
|
209
209
|
adam/sql/lark_parser.py,sha256=oNuPKfVTS1QBJ0wB3OXhBydmzgdCDED3LAheRM5cbiM,21378
|
|
210
210
|
adam/sql/sql_completer.py,sha256=RO-S4OiBuDPUL8EWiymB8JexIwgwNXx1QzErDxuL3y4,3939
|
|
211
211
|
adam/sql/sql_state_machine.py,sha256=3m8JsIg_tojwaHY5AdPl6UEE4ZnB5hbhOOApFfozhVo,39144
|
|
@@ -243,8 +243,11 @@ adam/utils_repl/appendable_completer.py,sha256=byLO9pZXExi13i6-fKY6gNzK-v0RtxIQt
|
|
|
243
243
|
adam/utils_repl/automata_completer.py,sha256=LJP_2WHHR7AtjX00MJ59VGQEL3t0XS-qYnDmMaZe-Tk,1641
|
|
244
244
|
adam/utils_repl/repl_completer.py,sha256=Zrs3RVVP7k6zacj1pKpYpSF6LOpziEA15yIuc5Nj3_4,6544
|
|
245
245
|
adam/utils_repl/state_machine.py,sha256=kO4_oSi_M53f3QQjINzzb2VFptjbnqX3KRC0G8LqqeA,5426
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
kaqing-2.0.
|
|
250
|
-
kaqing-2.0.
|
|
246
|
+
teddy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
247
|
+
teddy/lark_parser.py,sha256=1ZasiCM94C0nOA4HFJX7uMj0h2TReCMqLd1Wx9C50uA,14701
|
|
248
|
+
teddy/lark_parser2.py,sha256=tXM6D8BimgG4FK5ANYS2K8MoATdcYed5QOKXivbPQHw,21499
|
|
249
|
+
kaqing-2.0.188.dist-info/METADATA,sha256=QG9vHSpDDVhqBqKybXo3hiyXNIzrBhfU_IX-Kb6ZMdI,133
|
|
250
|
+
kaqing-2.0.188.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
251
|
+
kaqing-2.0.188.dist-info/entry_points.txt,sha256=SkzhuQJUWsXOzHeZ5TgQ2c3_g53UGK23zzJU_JTZOZI,39
|
|
252
|
+
kaqing-2.0.188.dist-info/top_level.txt,sha256=spQlE6mz0lPv3DfQLw8FenXyU0O-P8pi_FUCjdI2H9s,11
|
|
253
|
+
kaqing-2.0.188.dist-info/RECORD,,
|
teddy/__init__.py
ADDED
|
File without changes
|
teddy/lark_parser.py
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
from copy import copy
|
|
2
|
+
import re
|
|
3
|
+
from typing import Union, cast
|
|
4
|
+
from lark import Lark, Token, Tree
|
|
5
|
+
from lark.lexer import TerminalDef
|
|
6
|
+
from lark.grammar import NonTerminal, Terminal
|
|
7
|
+
|
|
8
|
+
def log(msg: str = None, debug: bool = False, depth: int = 0):
|
|
9
|
+
if not msg:
|
|
10
|
+
print()
|
|
11
|
+
return
|
|
12
|
+
|
|
13
|
+
if debug:
|
|
14
|
+
print(depth * ' ' + msg)
|
|
15
|
+
|
|
16
|
+
class GNode:
|
|
17
|
+
def __init__(self, name: str, token: Token = None, choices: list[int] = []):
|
|
18
|
+
self.name = name
|
|
19
|
+
self.token = token
|
|
20
|
+
self.choices = choices
|
|
21
|
+
|
|
22
|
+
def __eq__(self, other: 'GNode'):
|
|
23
|
+
return self.__repr__() == other.__repr__()
|
|
24
|
+
|
|
25
|
+
def __hash__(self):
|
|
26
|
+
return hash(self.__repr__())
|
|
27
|
+
|
|
28
|
+
def __repr__(self):
|
|
29
|
+
n = self.name if self.name else f'{self.token}'
|
|
30
|
+
if self.choices:
|
|
31
|
+
n = f'{n}-{"-".join([f"{c}" for c in self.choices])}'
|
|
32
|
+
|
|
33
|
+
return n
|
|
34
|
+
|
|
35
|
+
def pname(self):
|
|
36
|
+
return self.name if self.name else f'{self.token}'
|
|
37
|
+
|
|
38
|
+
def choice(self, depth: int):
|
|
39
|
+
if depth < len(self.choices):
|
|
40
|
+
return self.choices[depth]
|
|
41
|
+
|
|
42
|
+
return -1
|
|
43
|
+
|
|
44
|
+
def drop_last_choice(self):
|
|
45
|
+
new_node = copy(self)
|
|
46
|
+
new_node.choices = self.choices[:-1]
|
|
47
|
+
|
|
48
|
+
return new_node
|
|
49
|
+
|
|
50
|
+
def parse(s: str):
|
|
51
|
+
name_n_choices = s.split('-')
|
|
52
|
+
choices = []
|
|
53
|
+
if len(name_n_choices) > 1:
|
|
54
|
+
choices = {int(k) for k in name_n_choices[1:]}
|
|
55
|
+
|
|
56
|
+
return GNode(name_n_choices[0], choices=choices)
|
|
57
|
+
|
|
58
|
+
def add_choice(self, choice: int):
|
|
59
|
+
self.choices.append(choice)
|
|
60
|
+
|
|
61
|
+
class GPath:
|
|
62
|
+
def __init__(self, nodes: list[GNode], complete: bool):
|
|
63
|
+
self.nodes = nodes
|
|
64
|
+
self.complete = complete
|
|
65
|
+
|
|
66
|
+
def __eq__(self, other: 'GPath'):
|
|
67
|
+
return self.__repr__() == other.__repr__()
|
|
68
|
+
|
|
69
|
+
def __hash__(self):
|
|
70
|
+
return hash(self.__repr__())
|
|
71
|
+
|
|
72
|
+
def __repr__(self):
|
|
73
|
+
r = '.'.join([f'{p}' for p in self.nodes])
|
|
74
|
+
|
|
75
|
+
return r
|
|
76
|
+
|
|
77
|
+
def token(self):
|
|
78
|
+
return self.nodes[-1] if self.nodes else GNode(None, choices={})
|
|
79
|
+
|
|
80
|
+
def append(self, node):
|
|
81
|
+
new_path = copy(self)
|
|
82
|
+
nodes = []
|
|
83
|
+
for n in self.nodes:
|
|
84
|
+
n1 = copy(n)
|
|
85
|
+
ss = []
|
|
86
|
+
for k in n.choices:
|
|
87
|
+
ss.append(k)
|
|
88
|
+
n1.choices = ss
|
|
89
|
+
nodes.append(n1)
|
|
90
|
+
nodes.append(node)
|
|
91
|
+
new_path.nodes = nodes
|
|
92
|
+
|
|
93
|
+
return new_path
|
|
94
|
+
|
|
95
|
+
def drop_last(self):
|
|
96
|
+
new_path = copy(self)
|
|
97
|
+
nodes = []
|
|
98
|
+
for n in self.nodes[:-1]:
|
|
99
|
+
nodes.append(n)
|
|
100
|
+
|
|
101
|
+
new_path.nodes = nodes
|
|
102
|
+
|
|
103
|
+
return new_path
|
|
104
|
+
|
|
105
|
+
def clone(self):
|
|
106
|
+
new_path = copy(self)
|
|
107
|
+
nodes = []
|
|
108
|
+
for n in self.nodes:
|
|
109
|
+
n1 = copy(n)
|
|
110
|
+
ss = []
|
|
111
|
+
for k in n.choices:
|
|
112
|
+
ss.append(k)
|
|
113
|
+
n1.choices = ss
|
|
114
|
+
nodes.append(n1)
|
|
115
|
+
new_path.nodes = nodes
|
|
116
|
+
|
|
117
|
+
return new_path
|
|
118
|
+
|
|
119
|
+
def terminal(self):
|
|
120
|
+
last_node = self.nodes[-1]
|
|
121
|
+
return last_node.pname()
|
|
122
|
+
|
|
123
|
+
def terminals(paths: set['GPath']):
|
|
124
|
+
return ','.join(p.terminal() for p in list(paths))
|
|
125
|
+
|
|
126
|
+
def with_new_choice(self, choice: int):
|
|
127
|
+
new_path = self.clone()
|
|
128
|
+
new_path.nodes[-1].add_choice(choice)
|
|
129
|
+
|
|
130
|
+
return new_path
|
|
131
|
+
|
|
132
|
+
def with_next_choice(self):
|
|
133
|
+
new_path = self.clone()
|
|
134
|
+
new_path.nodes[-1].choices[-1] += 1
|
|
135
|
+
|
|
136
|
+
return new_path
|
|
137
|
+
|
|
138
|
+
def drop_last_choice(self):
|
|
139
|
+
new_path = self.clone()
|
|
140
|
+
new_path.nodes[-1] = new_path.nodes[-1].drop_last_choice()
|
|
141
|
+
|
|
142
|
+
return new_path
|
|
143
|
+
|
|
144
|
+
class LarkParser:
|
|
145
|
+
show_returns = False
|
|
146
|
+
|
|
147
|
+
def __init__(self, grammar: str = None):
|
|
148
|
+
if not grammar:
|
|
149
|
+
grammar = """
|
|
150
|
+
start: expression
|
|
151
|
+
expression: term (("+" | "-") term)*
|
|
152
|
+
term: factor (("*" | "/") factor)*
|
|
153
|
+
factor: NUMBER | "(" expression ")"
|
|
154
|
+
NUMBER: /[0-9]+/
|
|
155
|
+
%ignore " "
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
parser = Lark(grammar, start='start')
|
|
159
|
+
self.parser = parser
|
|
160
|
+
|
|
161
|
+
self.rules = {rule_def[0]: rule_def[2] for rule_def in parser.grammar.rule_defs}
|
|
162
|
+
self.terminals = {rule_def[0]: rule_def[1] for rule_def in parser.grammar.term_defs}
|
|
163
|
+
self.rules_by_name1 = {rule_def[0]: rule_def[1] for rule_def in parser.grammar.rule_defs}
|
|
164
|
+
self.rules_by_name2 = {rule_def[0]: rule_def[3] for rule_def in parser.grammar.rule_defs}
|
|
165
|
+
|
|
166
|
+
self.trees: dict[str, Tree] = {}
|
|
167
|
+
|
|
168
|
+
def find_next_terminals(self, path: GPath, debug=False):
|
|
169
|
+
if path == 'start':
|
|
170
|
+
return self.visit_current(LarkParser.build_path(path), debug=debug)
|
|
171
|
+
|
|
172
|
+
if isinstance(path, set):
|
|
173
|
+
paths = set()
|
|
174
|
+
|
|
175
|
+
for p in list(path):
|
|
176
|
+
paths |= self.visit_next(p, debug=debug)
|
|
177
|
+
|
|
178
|
+
return paths
|
|
179
|
+
elif isinstance(path, str):
|
|
180
|
+
path = LarkParser.build_path(path)
|
|
181
|
+
|
|
182
|
+
if debug: print(f'find_next_terminals({path}, {path.token().token})')
|
|
183
|
+
|
|
184
|
+
return self.visit_next(path, debug=debug)
|
|
185
|
+
|
|
186
|
+
def visit_current(self, path: GPath, tree: Tree = None, debug=False):
|
|
187
|
+
paths: set[GPath] = set()
|
|
188
|
+
|
|
189
|
+
if debug: print(f'visit_expansion({path}, {path.token().token}), {path.nodes[-1].choices}')
|
|
190
|
+
if not tree:
|
|
191
|
+
tree, _ = self.tree_by_choices(path, debug=debug)
|
|
192
|
+
|
|
193
|
+
if isinstance(tree, Token):
|
|
194
|
+
if debug: print('token', tree)
|
|
195
|
+
if path.nodes[-1].token:
|
|
196
|
+
path = path.drop_last()
|
|
197
|
+
|
|
198
|
+
p = path.append(GNode(name=self.token_name_from_raw(f'{tree}'), token=tree, choices=[]))
|
|
199
|
+
if LarkParser.show_returns: print('<- ', tree.value, '\t\t', p)
|
|
200
|
+
paths.add(p)
|
|
201
|
+
elif isinstance(tree, Terminal):
|
|
202
|
+
if debug: print('terminal', tree.name)
|
|
203
|
+
p = path.append(GNode(tree.name, choices=[]))
|
|
204
|
+
if LarkParser.show_returns: print('<- ', tree.name, '\t\t', p)
|
|
205
|
+
paths.add(p)
|
|
206
|
+
elif isinstance(tree, NonTerminal):
|
|
207
|
+
if debug: print('non-termial', tree.name, tree)
|
|
208
|
+
paths |= self.visit_current(path.append(GNode(tree.name, choices=[])), debug=debug)
|
|
209
|
+
elif isinstance(tree, tuple):
|
|
210
|
+
paths |= self.visit_current(path, tree=tree[0], debug=debug)
|
|
211
|
+
elif not tree:
|
|
212
|
+
raise Exception(f'null tree at {path}')
|
|
213
|
+
elif tree.data == 'expr':
|
|
214
|
+
paths |= self.visit_current(path.with_new_choice(0), tree=tree.children[0], debug=debug)
|
|
215
|
+
|
|
216
|
+
if cast(Token, tree.children[1]).value in ['*', '?']:
|
|
217
|
+
paths |= self.visit_next(path, debug=debug)
|
|
218
|
+
elif tree.data == 'value':
|
|
219
|
+
# print('value')
|
|
220
|
+
for child in tree.children:
|
|
221
|
+
paths |= self.visit_current(path, tree=child, debug=debug)
|
|
222
|
+
elif tree.data == 'range':
|
|
223
|
+
# print('range')
|
|
224
|
+
token0: Token = tree.children[0]
|
|
225
|
+
token1: Token = tree.children[1]
|
|
226
|
+
if token0.value == '"0"' and token1.value == '"9"':
|
|
227
|
+
p = path.append(GNode(name=self.token_name_from_raw(f'{token0}'), token=token0, choices=[]))
|
|
228
|
+
if LarkParser.show_returns: print('<- ', 0, '\t\t', p)
|
|
229
|
+
paths.add(p)
|
|
230
|
+
else:
|
|
231
|
+
raise Exception('not implented')
|
|
232
|
+
for i in range(ord(token0.value.strip('"')), ord(token1.value.strip('"')) + 1):
|
|
233
|
+
ch = chr(i)
|
|
234
|
+
p = path.append(GNode(name=self.token_name_from_raw(f'{token0}'), token=token0, choices=[]))
|
|
235
|
+
# p = path.append(GNode(name=self.token_name_from_raw(f'{tree}'), token=tree, choices=[]))
|
|
236
|
+
if LarkParser.show_returns: print('<- ', ch, '\t\t', p)
|
|
237
|
+
paths.add(p)
|
|
238
|
+
elif tree.data == 'literal':
|
|
239
|
+
# print('literal')
|
|
240
|
+
for child in tree.children:
|
|
241
|
+
paths |= self.visit_current(path, tree=child, debug=debug)
|
|
242
|
+
elif tree.data == 'expansions':
|
|
243
|
+
for i, child in enumerate(tree.children):
|
|
244
|
+
paths |= self.visit_current(path.with_new_choice(i), tree=child, debug=debug)
|
|
245
|
+
elif tree.data == 'expansion':
|
|
246
|
+
paths |= self.visit_current(path.with_new_choice(0), tree=tree.children[0], debug=debug)
|
|
247
|
+
|
|
248
|
+
return paths
|
|
249
|
+
|
|
250
|
+
def visit_next(self, path: GPath, debug=False):
|
|
251
|
+
paths: set[GPath] = set()
|
|
252
|
+
|
|
253
|
+
if not path.nodes:
|
|
254
|
+
return paths
|
|
255
|
+
|
|
256
|
+
path = self.strip_terminal_nodes(path)
|
|
257
|
+
node = path.nodes[-1]
|
|
258
|
+
|
|
259
|
+
me: Tree
|
|
260
|
+
parent: Tree
|
|
261
|
+
me, parent = self.tree_by_choices(path)
|
|
262
|
+
|
|
263
|
+
check_parent = True
|
|
264
|
+
if isinstance(me, Tree) and me.data == 'expr' and me.children[1] == '*':
|
|
265
|
+
if debug: print(' add expr repeat ', path)
|
|
266
|
+
paths |= self.visit_current(path.with_new_choice(0), debug=debug)
|
|
267
|
+
|
|
268
|
+
if isinstance(parent, Tree) and parent.data == 'expansion' and (node.choices[-1] + 1) < len(parent.children):
|
|
269
|
+
np = path.with_next_choice()
|
|
270
|
+
if debug: print(' move to next sibling ', np)
|
|
271
|
+
paths |= self.visit_current(np, debug=debug)
|
|
272
|
+
check_parent = False
|
|
273
|
+
|
|
274
|
+
if check_parent:
|
|
275
|
+
if len(node.choices) > 0:
|
|
276
|
+
p = path.drop_last_choice()
|
|
277
|
+
if debug: print(' move up to parent tree', p)
|
|
278
|
+
paths |= self.visit_next(p, debug=debug)
|
|
279
|
+
else:
|
|
280
|
+
path = path.drop_last()
|
|
281
|
+
if debug: print(' move up to parent node', path)
|
|
282
|
+
paths |= self.visit_next(path, debug=debug)
|
|
283
|
+
|
|
284
|
+
return paths
|
|
285
|
+
|
|
286
|
+
def tree_by_choices(self, path: GPath, debug=False):
|
|
287
|
+
debug = False
|
|
288
|
+
|
|
289
|
+
c = f'{path.nodes[-1]}'
|
|
290
|
+
p = f'{path.nodes[-1].drop_last_choice()}'
|
|
291
|
+
if c not in self.trees:
|
|
292
|
+
for key, value in self.trees_by_choices(path, debug=debug).items():
|
|
293
|
+
if not key:
|
|
294
|
+
self.trees[f'{path.token().name}'] = value
|
|
295
|
+
else:
|
|
296
|
+
self.trees[f'{path.token().name}-{key}'] = value
|
|
297
|
+
|
|
298
|
+
return self.trees[c], self.trees[p]
|
|
299
|
+
|
|
300
|
+
def trees_by_choices(self, path_or_tree: Union[GPath, Tree], choices: list[int] = [], depth: int = 0, debug=False):
|
|
301
|
+
if debug: print(f'trees_by_choices({path_or_tree}, {choices})')
|
|
302
|
+
|
|
303
|
+
if isinstance(path_or_tree, GPath):
|
|
304
|
+
n = self.find_last_rule(path_or_tree)
|
|
305
|
+
tree = self.rules[n.name]
|
|
306
|
+
else:
|
|
307
|
+
tree = path_or_tree
|
|
308
|
+
|
|
309
|
+
trees: dict[str, Tree] = {}
|
|
310
|
+
|
|
311
|
+
if isinstance(tree, Token):
|
|
312
|
+
if debug: log('tree', debug, depth)
|
|
313
|
+
pass
|
|
314
|
+
elif isinstance(tree, Terminal):
|
|
315
|
+
if debug: log('terminal', debug, depth)
|
|
316
|
+
|
|
317
|
+
choices_str = '-'.join([f"{c}" for c in choices])
|
|
318
|
+
trees[choices_str] = tree
|
|
319
|
+
|
|
320
|
+
pass
|
|
321
|
+
elif isinstance(tree, NonTerminal):
|
|
322
|
+
if debug: log('non-terminal', debug, depth)
|
|
323
|
+
pass
|
|
324
|
+
elif isinstance(tree, tuple):
|
|
325
|
+
if debug: log('tuple', debug, depth)
|
|
326
|
+
trees |= self.trees_by_choices(tree[0], choices, depth=depth, debug=debug)
|
|
327
|
+
elif tree.data == 'expr':
|
|
328
|
+
if debug: log('expr', debug, depth)
|
|
329
|
+
choices_str = '-'.join([f"{c}" for c in choices])
|
|
330
|
+
trees[choices_str] = tree
|
|
331
|
+
|
|
332
|
+
for child in tree.children[:1]:
|
|
333
|
+
trees |= self.trees_by_choices(child, choices + [0], depth=depth+1, debug=debug)
|
|
334
|
+
elif tree.data == 'value':
|
|
335
|
+
if debug: log('value', debug, depth)
|
|
336
|
+
|
|
337
|
+
choices_str = '-'.join([f"{c}" for c in choices])
|
|
338
|
+
trees[choices_str] = tree
|
|
339
|
+
|
|
340
|
+
for child in tree.children:
|
|
341
|
+
trees |= self.trees_by_choices(child, choices, depth=depth+1, debug=debug)
|
|
342
|
+
elif tree.data == 'literal':
|
|
343
|
+
if debug: log('literal', debug, depth)
|
|
344
|
+
|
|
345
|
+
choices_str = '-'.join([f"{c}" for c in choices])
|
|
346
|
+
trees[choices_str] = tree
|
|
347
|
+
|
|
348
|
+
for child in tree.children:
|
|
349
|
+
trees |= self.trees_by_choices(child, choices, depth=depth+1, debug=debug)
|
|
350
|
+
elif tree.data == 'expansions':
|
|
351
|
+
if debug: log('expansions', debug, depth)
|
|
352
|
+
|
|
353
|
+
choices_str = '-'.join([f"{c}" for c in choices])
|
|
354
|
+
trees[choices_str] = tree
|
|
355
|
+
for i, child in enumerate(tree.children):
|
|
356
|
+
trees |= self.trees_by_choices(child, choices + [i], depth=depth+1, debug=debug)
|
|
357
|
+
elif tree.data == 'expansion':
|
|
358
|
+
if debug: log('expansion', debug, depth)
|
|
359
|
+
|
|
360
|
+
choices_str = '-'.join([f"{c}" for c in choices])
|
|
361
|
+
trees[choices_str] = tree
|
|
362
|
+
for i, child in enumerate(tree.children):
|
|
363
|
+
trees |= self.trees_by_choices(child, choices + [i], depth=depth+1, debug=debug)
|
|
364
|
+
else:
|
|
365
|
+
if debug: log('else', debug, depth)
|
|
366
|
+
|
|
367
|
+
return trees
|
|
368
|
+
|
|
369
|
+
def strip_terminal_nodes(self, path: GPath):
|
|
370
|
+
while path.nodes[-1].name not in self.rules:
|
|
371
|
+
path = path.drop_last()
|
|
372
|
+
|
|
373
|
+
return path
|
|
374
|
+
|
|
375
|
+
def find_last_rule(self, path: GPath):
|
|
376
|
+
for n in reversed(path.nodes):
|
|
377
|
+
if n.name in self.rules:
|
|
378
|
+
return n
|
|
379
|
+
|
|
380
|
+
return None
|
|
381
|
+
|
|
382
|
+
def build_path(p: str):
|
|
383
|
+
nodes = []
|
|
384
|
+
for n in p.split('.'):
|
|
385
|
+
name_n_choices = n.split('-')
|
|
386
|
+
nodes.append(GNode(name_n_choices[0], choices=[int(c) for c in name_n_choices[1:]]))
|
|
387
|
+
|
|
388
|
+
return GPath(nodes, False)
|
|
389
|
+
|
|
390
|
+
def token_name(self, token):
|
|
391
|
+
td: TerminalDef = None
|
|
392
|
+
for t in reversed(self.parser.terminals):
|
|
393
|
+
td = t
|
|
394
|
+
# print(td)
|
|
395
|
+
if td.pattern.raw == '/./':
|
|
396
|
+
if token == '.':
|
|
397
|
+
return td.name
|
|
398
|
+
elif re.fullmatch(td.pattern.to_regexp(), token):
|
|
399
|
+
return td.name
|
|
400
|
+
|
|
401
|
+
raise Exception('cannot resolve string to a token')
|
|
402
|
+
|
|
403
|
+
def token_name_from_raw(self, raw_token):
|
|
404
|
+
td: TerminalDef = None
|
|
405
|
+
for t in reversed(self.parser.terminals):
|
|
406
|
+
if t.pattern.raw == raw_token:
|
|
407
|
+
td = t
|
|
408
|
+
return td.name
|
|
409
|
+
|
|
410
|
+
return self.token_name(raw_token.strip('"'))
|
|
411
|
+
|
|
412
|
+
def parse(self, s: str, debug=False):
|
|
413
|
+
ps = 'start'
|
|
414
|
+
ps = self.find_next_terminals(ps, debug=debug)
|
|
415
|
+
|
|
416
|
+
for token in self.parser.lex(s):
|
|
417
|
+
token_name = self.token_name(token)
|
|
418
|
+
|
|
419
|
+
ps = self.choose(ps, token_name)
|
|
420
|
+
ps = self.find_next_terminals(ps, debug=debug)
|
|
421
|
+
|
|
422
|
+
terminals = {p.terminal().strip('"') for p in ps}
|
|
423
|
+
|
|
424
|
+
# print(terminals)
|
|
425
|
+
|
|
426
|
+
return terminals
|
|
427
|
+
|
|
428
|
+
def choose(self, paths: list[GPath], token: str):
|
|
429
|
+
if LarkParser.show_returns: print(f'\n{token}\t-----------------------------')
|
|
430
|
+
|
|
431
|
+
ps = set()
|
|
432
|
+
for p in paths:
|
|
433
|
+
if f'{p.token()}' == token:
|
|
434
|
+
ps.add(p)
|
|
435
|
+
|
|
436
|
+
return ps
|
teddy/lark_parser2.py
ADDED
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
from copy import copy
|
|
2
|
+
from typing import Union
|
|
3
|
+
import unittest
|
|
4
|
+
from lark import Lark, Token, Tree
|
|
5
|
+
from lark.grammar import NonTerminal, Terminal
|
|
6
|
+
|
|
7
|
+
class GNode:
|
|
8
|
+
def __init__(self, name: str, token: Token = None, choices: dict[int, int] = {}):
|
|
9
|
+
self.name = name
|
|
10
|
+
self.token = token
|
|
11
|
+
self.choices = choices
|
|
12
|
+
|
|
13
|
+
def __eq__(self, other: 'GNode'):
|
|
14
|
+
return self.__repr__() == other.__repr__()
|
|
15
|
+
|
|
16
|
+
def __hash__(self):
|
|
17
|
+
return hash(self.__repr__())
|
|
18
|
+
|
|
19
|
+
def __repr__(self):
|
|
20
|
+
n = self.name if self.name else f'{self.token}'
|
|
21
|
+
if self.choices:
|
|
22
|
+
# print(self.choices, self.choices_to_list())
|
|
23
|
+
choices = self.choices_to_list()
|
|
24
|
+
n = f'{n}-{"-".join([f"{c}" for c in choices])}'
|
|
25
|
+
|
|
26
|
+
return n
|
|
27
|
+
|
|
28
|
+
def pname(self):
|
|
29
|
+
return self.name if self.name else f'{self.token}'
|
|
30
|
+
|
|
31
|
+
def choice(self, depth: int):
|
|
32
|
+
if depth in self.choices:
|
|
33
|
+
return self.choices[depth]
|
|
34
|
+
|
|
35
|
+
return -1
|
|
36
|
+
|
|
37
|
+
def set_choice(self, depth: int, choice: int):
|
|
38
|
+
self.choices[depth] = choice
|
|
39
|
+
|
|
40
|
+
def choices_to_list(self):
|
|
41
|
+
choices = []
|
|
42
|
+
|
|
43
|
+
if self.choices:
|
|
44
|
+
for i in range(0, max(self.choices.keys()) + 1):
|
|
45
|
+
if i in self.choices:
|
|
46
|
+
choices.append(self.choices[i])
|
|
47
|
+
else:
|
|
48
|
+
choices.append(0)
|
|
49
|
+
|
|
50
|
+
return choices
|
|
51
|
+
|
|
52
|
+
def choice_list_to_str(self):
|
|
53
|
+
return '-'.join([f'{c}' for c in self.choices_to_list()[:-1]])
|
|
54
|
+
|
|
55
|
+
def choice_list_to_dict(self, choices: list[int]):
|
|
56
|
+
return {i: c for i, c in enumerate(choices)}
|
|
57
|
+
|
|
58
|
+
def drop_last(self):
|
|
59
|
+
new_node = copy(self)
|
|
60
|
+
choices = self.choices_to_list()[:-1]
|
|
61
|
+
new_node.choices = self.choice_list_to_dict(choices)
|
|
62
|
+
|
|
63
|
+
return new_node
|
|
64
|
+
|
|
65
|
+
class GPath:
|
|
66
|
+
def __init__(self, nodes: list[GNode], complete: bool, trail: list[str] = []):
|
|
67
|
+
self.nodes = nodes
|
|
68
|
+
self.complete = complete
|
|
69
|
+
self.trail = trail
|
|
70
|
+
|
|
71
|
+
def __eq__(self, other: 'GPath'):
|
|
72
|
+
return self.__repr__() == other.__repr__()
|
|
73
|
+
|
|
74
|
+
def __hash__(self):
|
|
75
|
+
return hash(self.__repr__())
|
|
76
|
+
|
|
77
|
+
def __repr__(self):
|
|
78
|
+
return '.'.join([f'{p}' for p in self.nodes])
|
|
79
|
+
|
|
80
|
+
def token(self):
|
|
81
|
+
return self.nodes[-1]
|
|
82
|
+
|
|
83
|
+
def append(self, node):
|
|
84
|
+
new_path = copy(self)
|
|
85
|
+
nodes = []
|
|
86
|
+
for n in self.nodes:
|
|
87
|
+
n1 = copy(n)
|
|
88
|
+
ss = {}
|
|
89
|
+
for k, v in n.choices.items():
|
|
90
|
+
ss[k] = v
|
|
91
|
+
n1.choices = ss
|
|
92
|
+
nodes.append(n1)
|
|
93
|
+
nodes.append(node)
|
|
94
|
+
new_path.nodes = nodes
|
|
95
|
+
|
|
96
|
+
return new_path
|
|
97
|
+
|
|
98
|
+
def drop_last(self):
|
|
99
|
+
new_path = copy(self)
|
|
100
|
+
nodes = []
|
|
101
|
+
for n in self.nodes[:-1]:
|
|
102
|
+
nodes.append(n)
|
|
103
|
+
new_path.nodes = nodes
|
|
104
|
+
|
|
105
|
+
return new_path
|
|
106
|
+
|
|
107
|
+
def clone(self):
|
|
108
|
+
new_path = copy(self)
|
|
109
|
+
nodes = []
|
|
110
|
+
for n in self.nodes:
|
|
111
|
+
n1 = copy(n)
|
|
112
|
+
ss = {}
|
|
113
|
+
for k, v in n.choices.items():
|
|
114
|
+
ss[k] = v
|
|
115
|
+
n1.choices = ss
|
|
116
|
+
nodes.append(n1)
|
|
117
|
+
new_path.nodes = nodes
|
|
118
|
+
|
|
119
|
+
return new_path
|
|
120
|
+
|
|
121
|
+
def terminal(self):
|
|
122
|
+
last_node = self.nodes[-1]
|
|
123
|
+
# print('SEAN', last_node.token.data)
|
|
124
|
+
return last_node.pname()
|
|
125
|
+
|
|
126
|
+
def terminals(paths: set['GPath']):
|
|
127
|
+
return ','.join(p.terminal() for p in list(paths))
|
|
128
|
+
|
|
129
|
+
class CompletePath:
|
|
130
|
+
def __init__(self, path: GPath, complete: False):
|
|
131
|
+
self.path = path
|
|
132
|
+
self.complete = complete
|
|
133
|
+
|
|
134
|
+
class SeqOrUnion:
|
|
135
|
+
def __init__(self, collection: list, is_seq: bool):
|
|
136
|
+
self.collection = collection
|
|
137
|
+
self.is_seq = is_seq
|
|
138
|
+
|
|
139
|
+
class LarkParser:
|
|
140
|
+
def __init__(self):
|
|
141
|
+
grammar = """
|
|
142
|
+
start: expression
|
|
143
|
+
expression: term (("+" | "-") term)*
|
|
144
|
+
term: factor (("*" | "/") factor)*
|
|
145
|
+
factor: NUMBER | "(" expression ")"
|
|
146
|
+
NUMBER: /[0-9]+/
|
|
147
|
+
%ignore " "
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
parser = Lark(grammar, start='start')
|
|
151
|
+
|
|
152
|
+
# print('TEST parsing', parser.parse('(56)'))
|
|
153
|
+
|
|
154
|
+
# print(parser.grammar.term_defs)
|
|
155
|
+
|
|
156
|
+
for rule in parser.grammar.rule_defs + parser.grammar.term_defs:
|
|
157
|
+
lhv = rule[0]
|
|
158
|
+
# print(f'{lhv} :=')
|
|
159
|
+
# print(rule[1])
|
|
160
|
+
if len(rule) > 2:
|
|
161
|
+
tree = rule[2]
|
|
162
|
+
# print(f'{tree.pretty()}')
|
|
163
|
+
# print(rule[3])
|
|
164
|
+
|
|
165
|
+
self.rules = {rule_def[0]: rule_def[2] for rule_def in parser.grammar.rule_defs}
|
|
166
|
+
self.terminals = {rule_def[0]: rule_def[1] for rule_def in parser.grammar.term_defs}
|
|
167
|
+
self.rules_by_name1 = {rule_def[0]: rule_def[1] for rule_def in parser.grammar.rule_defs}
|
|
168
|
+
self.rules_by_name2 = {rule_def[0]: rule_def[3] for rule_def in parser.grammar.rule_defs}
|
|
169
|
+
|
|
170
|
+
def children_by_choices(self, tree: Tree, choices: list[int] = [], depth: int = 0):
|
|
171
|
+
children: dict[str, SeqOrUnion] = {}
|
|
172
|
+
|
|
173
|
+
if isinstance(tree, Token):
|
|
174
|
+
pass
|
|
175
|
+
elif isinstance(tree, Terminal):
|
|
176
|
+
pass
|
|
177
|
+
elif isinstance(tree, NonTerminal):
|
|
178
|
+
pass
|
|
179
|
+
elif isinstance(tree, tuple):
|
|
180
|
+
children |= self.children_by_choices(tree[0], choices, depth=depth)
|
|
181
|
+
elif tree.data == 'expr':
|
|
182
|
+
for child in tree.children[:1]:
|
|
183
|
+
children |= self.children_by_choices(child, choices, depth=depth)
|
|
184
|
+
# complete = False
|
|
185
|
+
elif tree.data == 'value':
|
|
186
|
+
# print('value')
|
|
187
|
+
for child in tree.children:
|
|
188
|
+
children |= self.children_by_choices(child, choices, depth=depth)
|
|
189
|
+
elif tree.data == 'literal':
|
|
190
|
+
# print('literal')
|
|
191
|
+
for child in tree.children:
|
|
192
|
+
children |= self.children_by_choices(child, choices, depth=depth)
|
|
193
|
+
elif tree.data == 'expansions':
|
|
194
|
+
# print('expansions')
|
|
195
|
+
# print(tree.pretty())
|
|
196
|
+
choices_str = '-'.join([f"{c}" for c in choices])
|
|
197
|
+
children[choices_str] = SeqOrUnion(tree.children, False)
|
|
198
|
+
# print(f'children_by_choices[{choices_str}] = {len(tree.children)}')
|
|
199
|
+
for i, child in enumerate(tree.children):
|
|
200
|
+
children |= self.children_by_choices(child, choices + [i], depth=depth+1)
|
|
201
|
+
elif tree.data == 'expansion':
|
|
202
|
+
# print('expansion')
|
|
203
|
+
# print(tree.pretty())
|
|
204
|
+
choices_str = '-'.join([f"{c}" for c in choices])
|
|
205
|
+
children[choices_str] = SeqOrUnion(tree.children, True)
|
|
206
|
+
# print(f'children_by_choices[{choices_str}] = {len(tree.children)}')
|
|
207
|
+
for i, child in enumerate(tree.children):
|
|
208
|
+
children |= self.children_by_choices(child, choices + [i], depth=depth+1)
|
|
209
|
+
|
|
210
|
+
return children
|
|
211
|
+
|
|
212
|
+
def find_next_terminals(self, path: GPath, debug=False):
|
|
213
|
+
if path == 'start':
|
|
214
|
+
return self.find_terminals(path, debug=debug)
|
|
215
|
+
|
|
216
|
+
if isinstance(path, set):
|
|
217
|
+
paths = set()
|
|
218
|
+
|
|
219
|
+
for p in list(path):
|
|
220
|
+
paths |= self.find_next_terminals(p, debug=debug)
|
|
221
|
+
|
|
222
|
+
return paths
|
|
223
|
+
elif isinstance(path, str):
|
|
224
|
+
path = LarkParser.build_path(path)
|
|
225
|
+
|
|
226
|
+
terminals = self.find_terminals(path, debug=debug)
|
|
227
|
+
|
|
228
|
+
next_terminals = set()
|
|
229
|
+
|
|
230
|
+
for t in terminals:
|
|
231
|
+
paths = self.find_next(t)
|
|
232
|
+
for path in paths:
|
|
233
|
+
terms = self.find_terminals(path, debug=debug)
|
|
234
|
+
for term in terms:
|
|
235
|
+
term.trail.append(term.terminal())
|
|
236
|
+
next_terminals |= terms
|
|
237
|
+
|
|
238
|
+
return next_terminals
|
|
239
|
+
|
|
240
|
+
def _find_next_terminals(self, path: GPath, debug=False):
|
|
241
|
+
# terminals = self.find_terminals(path)
|
|
242
|
+
|
|
243
|
+
if isinstance(path, str):
|
|
244
|
+
path = LarkParser.build_path(path)
|
|
245
|
+
|
|
246
|
+
next_terminals = set()
|
|
247
|
+
|
|
248
|
+
# for t in terminals:
|
|
249
|
+
paths = self.find_next(path)
|
|
250
|
+
for path in paths:
|
|
251
|
+
next_terminals |= self.find_terminals(path, debug=debug)
|
|
252
|
+
|
|
253
|
+
return next_terminals
|
|
254
|
+
|
|
255
|
+
def find_next(self, path: GPath, depth: int = -1, debug=False):
|
|
256
|
+
paths: set[GPath] = set()
|
|
257
|
+
|
|
258
|
+
if not path.nodes:
|
|
259
|
+
return paths
|
|
260
|
+
|
|
261
|
+
if depth == -1:
|
|
262
|
+
if debug: print('find_next', path)
|
|
263
|
+
|
|
264
|
+
node = path.nodes[-1]
|
|
265
|
+
if node.name in self.rules:
|
|
266
|
+
tree = self.rules[node.name]
|
|
267
|
+
elif node.name in self.terminals:
|
|
268
|
+
tree = self.terminals[node.name]
|
|
269
|
+
else:
|
|
270
|
+
tree = node.token
|
|
271
|
+
|
|
272
|
+
choices = node.choices_to_list()
|
|
273
|
+
children = self.children_by_choices(tree)
|
|
274
|
+
# print('children', children)
|
|
275
|
+
|
|
276
|
+
if depth == -1:
|
|
277
|
+
depth = len(choices) - 1
|
|
278
|
+
|
|
279
|
+
# print(depth, len(choices), len(children[node.choice_list_to_str()]))
|
|
280
|
+
# print(len(choices), len(children))
|
|
281
|
+
check_parent = True
|
|
282
|
+
if children and children[node.choice_list_to_str()].is_seq and choices[depth] + 1 < len(children[node.choice_list_to_str()].collection):
|
|
283
|
+
node.choices[depth] += 1
|
|
284
|
+
if debug: print(' move to next sibling ', path)
|
|
285
|
+
# print('paths', path.nodes[-1] )
|
|
286
|
+
paths.add(path.clone())
|
|
287
|
+
check_parent = False
|
|
288
|
+
|
|
289
|
+
next_sibing = children[node.choice_list_to_str()].collection[node.choices[depth]]
|
|
290
|
+
if next_sibing.data == 'expr':
|
|
291
|
+
paths |= self.find_next(path, depth, debug=debug)
|
|
292
|
+
check_parent = True
|
|
293
|
+
|
|
294
|
+
# print('next_sibling', next_sibing)
|
|
295
|
+
|
|
296
|
+
if check_parent:
|
|
297
|
+
if depth > 0:
|
|
298
|
+
new_node = node.drop_last()
|
|
299
|
+
path.nodes[-1] = new_node
|
|
300
|
+
if debug: print(' move up to parent tree', new_node)
|
|
301
|
+
depth -= 1
|
|
302
|
+
paths |= self.find_next(path, depth, debug=debug)
|
|
303
|
+
else:
|
|
304
|
+
path = path.drop_last()
|
|
305
|
+
if debug: print(' move up to parent node', path)
|
|
306
|
+
paths |= self.find_next(path, -1, debug=debug)
|
|
307
|
+
|
|
308
|
+
return paths
|
|
309
|
+
|
|
310
|
+
def find_terminals(self, path: GPath, paths: set[GPath] = set(), path_history: set[GNode] = set(), debug=False):
|
|
311
|
+
if isinstance(path, str):
|
|
312
|
+
path = LarkParser.build_path(path)
|
|
313
|
+
|
|
314
|
+
if debug: print('find_terminals', path)
|
|
315
|
+
|
|
316
|
+
path_history.add(path.nodes[-1])
|
|
317
|
+
|
|
318
|
+
paths = self._find_terminals(None, path, debug=debug)
|
|
319
|
+
|
|
320
|
+
new_paths = set()
|
|
321
|
+
for p in paths:
|
|
322
|
+
node = p.nodes[-1]
|
|
323
|
+
if node.token:
|
|
324
|
+
# print('found terminal node', node)
|
|
325
|
+
new_paths.add(p)
|
|
326
|
+
else:
|
|
327
|
+
new_paths |= self.find_terminals(p, paths, path_history, debug=debug)
|
|
328
|
+
|
|
329
|
+
return new_paths
|
|
330
|
+
|
|
331
|
+
def _find_terminals(self, tree: Tree, path: GPath, depth = 0, debug=False):
|
|
332
|
+
paths: set[GPath] = set()
|
|
333
|
+
# complete = False
|
|
334
|
+
|
|
335
|
+
node = path.nodes[-1]
|
|
336
|
+
if not tree:
|
|
337
|
+
if node.name in self.rules:
|
|
338
|
+
tree = self.rules[node.name]
|
|
339
|
+
elif node.name in self.terminals:
|
|
340
|
+
tree = self.terminals[node.name]
|
|
341
|
+
else:
|
|
342
|
+
tree = node.token
|
|
343
|
+
|
|
344
|
+
# print(tree)
|
|
345
|
+
|
|
346
|
+
if isinstance(tree, Token):
|
|
347
|
+
p = path.append(GNode(name=None, token=tree))
|
|
348
|
+
print('<- ', tree.value, '\t\t', p)
|
|
349
|
+
paths.add(p)
|
|
350
|
+
elif isinstance(tree, Terminal):
|
|
351
|
+
paths.add(path.append(GNode(tree.name)))
|
|
352
|
+
elif isinstance(tree, NonTerminal):
|
|
353
|
+
# print('non-termial', tree.name)
|
|
354
|
+
paths.add(path.append(GNode(tree.name)))
|
|
355
|
+
elif isinstance(tree, tuple):
|
|
356
|
+
paths |= self._find_terminals(tree[0], path, depth=depth)
|
|
357
|
+
elif tree.data == 'expr':
|
|
358
|
+
# print('expr')
|
|
359
|
+
for child in tree.children[:1]:
|
|
360
|
+
paths |= self._find_terminals(child, path, depth=depth)
|
|
361
|
+
elif tree.data == 'value':
|
|
362
|
+
# print('value')
|
|
363
|
+
for child in tree.children:
|
|
364
|
+
paths |= self._find_terminals(child, path, depth=depth)
|
|
365
|
+
elif tree.data == 'literal':
|
|
366
|
+
# print('literal')
|
|
367
|
+
for child in tree.children:
|
|
368
|
+
paths |= self._find_terminals(child, path, depth=depth)
|
|
369
|
+
elif tree.data == 'expansions':
|
|
370
|
+
if node.choice(depth) == -1:
|
|
371
|
+
for i, child in enumerate(tree.children):
|
|
372
|
+
p = path.clone()
|
|
373
|
+
node = p.nodes[-1]
|
|
374
|
+
node.set_choice(depth, i)
|
|
375
|
+
paths |= self._find_terminals(child, p, depth=depth+1)
|
|
376
|
+
else:
|
|
377
|
+
# node.set_choice(depth, node.choice(depth))
|
|
378
|
+
paths |= self._find_terminals(tree.children[node.choice(depth)], path, depth=depth+1)
|
|
379
|
+
elif tree.data == 'expansion':
|
|
380
|
+
# print('expansion', path)
|
|
381
|
+
if (choice := node.choice(depth)) == -1:
|
|
382
|
+
choice = 0
|
|
383
|
+
|
|
384
|
+
node.set_choice(depth, choice)
|
|
385
|
+
paths |= self._find_terminals(tree.children[choice], path, depth=depth+1)
|
|
386
|
+
|
|
387
|
+
return paths
|
|
388
|
+
|
|
389
|
+
def visit(self, path: GPath, paths: set[GPath] = set(), path_history: set[GNode] = set(), next = False):
|
|
390
|
+
if isinstance(path, str):
|
|
391
|
+
print('visit', path)
|
|
392
|
+
path = LarkParser.build_path(path)
|
|
393
|
+
|
|
394
|
+
# print('visit', path)
|
|
395
|
+
if path.nodes[-1] in path_history:
|
|
396
|
+
return paths
|
|
397
|
+
|
|
398
|
+
path_history.add(path.nodes[-1])
|
|
399
|
+
|
|
400
|
+
paths = self.visit_tree(None, path, next=next)
|
|
401
|
+
|
|
402
|
+
new_paths = set()
|
|
403
|
+
for p in paths:
|
|
404
|
+
node = p.nodes[-1]
|
|
405
|
+
if node.token:
|
|
406
|
+
# print('found terminal node', node)
|
|
407
|
+
new_paths.add(p)
|
|
408
|
+
else:
|
|
409
|
+
new_paths |= self.visit(p, paths, path_history, next=False)
|
|
410
|
+
|
|
411
|
+
return new_paths
|
|
412
|
+
|
|
413
|
+
def visit_tree(self, tree: Tree, path: GPath, depth = 0, next = False):
|
|
414
|
+
paths: set[GPath] = set()
|
|
415
|
+
# complete = False
|
|
416
|
+
|
|
417
|
+
node = path.nodes[-1]
|
|
418
|
+
if not tree:
|
|
419
|
+
if node.name in self.rules:
|
|
420
|
+
tree = self.rules[node.name]
|
|
421
|
+
elif node.name in self.terminals:
|
|
422
|
+
tree = self.terminals[node.name]
|
|
423
|
+
else:
|
|
424
|
+
tree = node.token
|
|
425
|
+
|
|
426
|
+
# print(tree)
|
|
427
|
+
|
|
428
|
+
if isinstance(tree, Token):
|
|
429
|
+
p = path.append(GNode(name=None, token=tree))
|
|
430
|
+
print('<- ', tree.value, '\t\t', p)
|
|
431
|
+
paths.add(p)
|
|
432
|
+
elif isinstance(tree, Terminal):
|
|
433
|
+
paths.add(path.append(GNode(tree.name)))
|
|
434
|
+
elif isinstance(tree, NonTerminal):
|
|
435
|
+
# print('non-termial', tree.name)
|
|
436
|
+
paths.add(path.append(GNode(tree.name)))
|
|
437
|
+
elif isinstance(tree, tuple):
|
|
438
|
+
paths |= self.visit_tree(tree[0], path, depth=depth)
|
|
439
|
+
elif tree.data == 'expr':
|
|
440
|
+
# print('SEAN expr', tree.data, tree.children[1])
|
|
441
|
+
# print('expr add extra', path)
|
|
442
|
+
# paths.add(path)
|
|
443
|
+
for child in tree.children[:1]:
|
|
444
|
+
paths |= self.visit_tree(child, path, depth=depth)
|
|
445
|
+
# complete = False
|
|
446
|
+
elif tree.data == 'value':
|
|
447
|
+
# print('value')
|
|
448
|
+
for child in tree.children:
|
|
449
|
+
paths |= self.visit_tree(child, path, depth=depth)
|
|
450
|
+
elif tree.data == 'literal':
|
|
451
|
+
# print('literal')
|
|
452
|
+
for child in tree.children:
|
|
453
|
+
paths |= self.visit_tree(child, path, depth=depth)
|
|
454
|
+
elif tree.data == 'expansions':
|
|
455
|
+
# print('expansions', path)
|
|
456
|
+
if next:
|
|
457
|
+
paths |= self.visit_tree(tree.children[node.choice(depth)], path, depth=depth+1)
|
|
458
|
+
else:
|
|
459
|
+
for i, child in enumerate(tree.children):
|
|
460
|
+
node.set_choice(depth, i)
|
|
461
|
+
paths |= self.visit_tree(child, path, depth=depth+1)
|
|
462
|
+
elif tree.data == 'expansion':
|
|
463
|
+
# print('expansion', path)
|
|
464
|
+
if (choice := node.choice(depth)) == -1:
|
|
465
|
+
choice = 0
|
|
466
|
+
|
|
467
|
+
node.set_choice(depth, choice)
|
|
468
|
+
paths |= self.visit_tree(tree.children[choice], path, depth=depth+1)
|
|
469
|
+
|
|
470
|
+
return paths
|
|
471
|
+
|
|
472
|
+
def _find_next(self, path: GPath, paths: set[GPath] = set(), path_history: set[GNode] = set()):
|
|
473
|
+
if isinstance(path, str):
|
|
474
|
+
print('visit_next', path)
|
|
475
|
+
path = LarkParser.build_path(path)
|
|
476
|
+
|
|
477
|
+
# print('visit', path)
|
|
478
|
+
paths: set[GPath] = set()
|
|
479
|
+
|
|
480
|
+
for i in reversed(range(1, len(path.nodes) + 1)):
|
|
481
|
+
path = GPath(path.nodes[:i], False)
|
|
482
|
+
print('testing path', path)
|
|
483
|
+
|
|
484
|
+
complete = False
|
|
485
|
+
next = self.find_tree_next(None, path)
|
|
486
|
+
if next:
|
|
487
|
+
for n in next:
|
|
488
|
+
print('visiting with next', n)
|
|
489
|
+
paths |= self.visit(n, paths, set(), next=True)
|
|
490
|
+
if n.complete:
|
|
491
|
+
complete = True
|
|
492
|
+
else:
|
|
493
|
+
pass
|
|
494
|
+
# print('SEAN not complete', n)
|
|
495
|
+
# if not complete:
|
|
496
|
+
# break
|
|
497
|
+
# if next.complete:
|
|
498
|
+
# print('SEAN complete', next.complete)
|
|
499
|
+
# continue
|
|
500
|
+
if next and not complete:
|
|
501
|
+
break
|
|
502
|
+
|
|
503
|
+
# else:
|
|
504
|
+
# break
|
|
505
|
+
|
|
506
|
+
return paths
|
|
507
|
+
|
|
508
|
+
def find_tree_next(self, tree: Tree, path: GPath, depth = 0):
|
|
509
|
+
print('find_tree_next', path, depth)
|
|
510
|
+
paths: set[GPath] = set()
|
|
511
|
+
# complete = False
|
|
512
|
+
|
|
513
|
+
node = path.nodes[-1]
|
|
514
|
+
if not tree:
|
|
515
|
+
if node.name in self.rules:
|
|
516
|
+
tree = self.rules[node.name]
|
|
517
|
+
elif node.name in self.terminals:
|
|
518
|
+
tree = self.terminals[node.name]
|
|
519
|
+
else:
|
|
520
|
+
tree = node.token
|
|
521
|
+
|
|
522
|
+
if isinstance(tree, Token):
|
|
523
|
+
path.complete = True
|
|
524
|
+
pass
|
|
525
|
+
# print('token found', tree.value)
|
|
526
|
+
elif isinstance(tree, Terminal):
|
|
527
|
+
path.complete = True
|
|
528
|
+
pass
|
|
529
|
+
# paths.add(path.append(GNode(tree.name)))
|
|
530
|
+
elif isinstance(tree, NonTerminal):
|
|
531
|
+
pass
|
|
532
|
+
# print('non-termial', tree.name)
|
|
533
|
+
# paths.add(path.append(GNode(tree.name)))
|
|
534
|
+
elif isinstance(tree, tuple):
|
|
535
|
+
paths |= self.find_tree_next(tree[0], path, depth=depth)
|
|
536
|
+
elif tree.data == 'expr':
|
|
537
|
+
print(" " * depth, 'expr')
|
|
538
|
+
paths.add(path)
|
|
539
|
+
for child in tree.children[:1]:
|
|
540
|
+
paths |= self.find_tree_next(child, path, depth=depth)
|
|
541
|
+
# complete = False
|
|
542
|
+
elif tree.data == 'value':
|
|
543
|
+
print(" " * depth, 'value')
|
|
544
|
+
for child in tree.children:
|
|
545
|
+
paths |= self.find_tree_next(child, path, depth=depth)
|
|
546
|
+
elif tree.data == 'literal':
|
|
547
|
+
print(" " * depth, 'literal')
|
|
548
|
+
for child in tree.children:
|
|
549
|
+
paths |= self.find_tree_next(child, path, depth=depth)
|
|
550
|
+
elif tree.data == 'expansions':
|
|
551
|
+
print(" " * depth, 'expansions')
|
|
552
|
+
child = tree.children[node.choice(depth)]
|
|
553
|
+
path = self.find_tree_next(child, path, depth=depth+1)
|
|
554
|
+
if node.choices[depth] + 1 < len(tree.children):
|
|
555
|
+
node.choices[depth] += 1
|
|
556
|
+
|
|
557
|
+
paths |= path
|
|
558
|
+
|
|
559
|
+
# for i, child in enumerate(tree.children):
|
|
560
|
+
# # self.set_next_choice(path.nodes[-1], depth)
|
|
561
|
+
# paths |= self.visit_tree_next(child, rules, terminals, path, depth=depth+1)
|
|
562
|
+
elif tree.data == 'expansion':
|
|
563
|
+
print(" " * depth, 'expansion')
|
|
564
|
+
child = tree.children[node.choice(depth)]
|
|
565
|
+
if depth < len(node.choices) -1:
|
|
566
|
+
path = self.find_tree_next(child, path, depth=depth+1)
|
|
567
|
+
if node.choices[depth] + 1 < len(tree.children):
|
|
568
|
+
node.choices[depth] += 1
|
|
569
|
+
|
|
570
|
+
paths |= path
|
|
571
|
+
# else:
|
|
572
|
+
# if len(tree.children) > node.choices[depth] + 1:
|
|
573
|
+
# node.choices[depth] += 1
|
|
574
|
+
# if tree.children[node.choices[depth]].data == 'expr':
|
|
575
|
+
# print('SEAN expr')
|
|
576
|
+
# # np = path.drop_last()
|
|
577
|
+
# # print('SEAN next choice', np)
|
|
578
|
+
# # paths.add(np)
|
|
579
|
+
# path.complete = True
|
|
580
|
+
# paths.add(path)
|
|
581
|
+
|
|
582
|
+
# # self.set_next_choice(node, depth)
|
|
583
|
+
# paths.add(path)
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
# print(" " * depth, 'expansion', path)
|
|
587
|
+
# print('SEAN expansion', tree.pretty())
|
|
588
|
+
# if len(tree.children) > node.choices[depth] + 1:
|
|
589
|
+
# # print('next child found', tree.children, depth)
|
|
590
|
+
|
|
591
|
+
# node.choices[depth] += 1
|
|
592
|
+
# if tree.children[node.choices[depth]].data == 'expr':
|
|
593
|
+
# print('SEAN expr')
|
|
594
|
+
# # np = path.drop_last()
|
|
595
|
+
# # print('SEAN next choice', np)
|
|
596
|
+
# # paths.add(np)
|
|
597
|
+
# path.complete = True
|
|
598
|
+
# paths.add(path)
|
|
599
|
+
|
|
600
|
+
# # self.set_next_choice(node, depth)
|
|
601
|
+
# paths.add(path)
|
|
602
|
+
|
|
603
|
+
# # print('SEAN returning', path)
|
|
604
|
+
|
|
605
|
+
# return paths
|
|
606
|
+
# else:
|
|
607
|
+
# path.complete = True
|
|
608
|
+
|
|
609
|
+
# print('returning', paths)
|
|
610
|
+
return paths
|
|
611
|
+
|
|
612
|
+
def build_path(p: str):
|
|
613
|
+
nodes = []
|
|
614
|
+
for n in p.split('.'):
|
|
615
|
+
name_n_choices = n.split('-')
|
|
616
|
+
nodes.append(GNode(name_n_choices[0], choices={int(k): int(v) for k, v in enumerate(name_n_choices[1:])}))
|
|
617
|
+
|
|
618
|
+
return GPath(nodes, False)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
adam
|
|
File without changes
|
|
File without changes
|