pytrilogy 0.0.3.4__py3-none-any.whl → 0.0.3.6__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 pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.3.4.dist-info → pytrilogy-0.0.3.6.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.3.4.dist-info → pytrilogy-0.0.3.6.dist-info}/RECORD +13 -13
- trilogy/__init__.py +1 -1
- trilogy/core/models/author.py +3 -7
- trilogy/core/models/environment.py +49 -25
- trilogy/core/processing/concept_strategies_v3.py +1 -1
- trilogy/core/query_processor.py +1 -1
- trilogy/executor.py +7 -5
- trilogy/parsing/parse_engine.py +38 -23
- {pytrilogy-0.0.3.4.dist-info → pytrilogy-0.0.3.6.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.4.dist-info → pytrilogy-0.0.3.6.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.4.dist-info → pytrilogy-0.0.3.6.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.4.dist-info → pytrilogy-0.0.3.6.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
trilogy/__init__.py,sha256=R9yJSDmZQvpxjWMcp4mGFnbg7xuxUCiIVrvP8eacKj4,302
|
|
2
2
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
trilogy/constants.py,sha256=qZ1d0hoKPPV2HHCoFwPYTVB7b6bXjpWvXd3lE-zEhy8,1494
|
|
4
4
|
trilogy/engine.py,sha256=yOPnR7XCjWG82Gym_LLZBkYKKJdLCvqdCyt8zguNcnM,1103
|
|
5
|
-
trilogy/executor.py,sha256=
|
|
5
|
+
trilogy/executor.py,sha256=sssEPDnIDPiQtMSrt5pFiJXUfcDc6gSi4m2Eliod_BM,16844
|
|
6
6
|
trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
|
|
7
7
|
trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
|
|
@@ -18,14 +18,14 @@ trilogy/core/functions.py,sha256=7Pq9jYSJd45L2pxT7AI-_rXVZmeLnmTPp8d1lA4z4Vk,244
|
|
|
18
18
|
trilogy/core/graph_models.py,sha256=z17EoO8oky2QOuO6E2aMWoVNKEVJFhLdsQZOhC4fNLU,2079
|
|
19
19
|
trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
|
|
20
20
|
trilogy/core/optimization.py,sha256=xGO8piVsLrpqrx-Aid_Y56_5slSv4eZmlP64hCHRiEc,7957
|
|
21
|
-
trilogy/core/query_processor.py,sha256=
|
|
21
|
+
trilogy/core/query_processor.py,sha256=Do8YpdPBdsbKtl9n37hobzk8SORMGqH-e_zNNxd-BE4,19456
|
|
22
22
|
trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
trilogy/core/models/author.py,sha256=
|
|
23
|
+
trilogy/core/models/author.py,sha256=oRCKWhz-i1fO1LlHWiHE3l1awCHdQ3yx6FKH9n9RxRU,67188
|
|
24
24
|
trilogy/core/models/build.py,sha256=kiq31T8LtUtgmT37m617Q2MlMvQTuAxJzwb6947EiWU,56127
|
|
25
25
|
trilogy/core/models/build_environment.py,sha256=8UggvlPU708GZWYPJMc_ou2r7M3TY2g69eqGvz03YX0,5528
|
|
26
26
|
trilogy/core/models/core.py,sha256=yie1uuq62uOQ5fjob9NMJbdvQPrCErXUT7JTCuYRyjI,9697
|
|
27
27
|
trilogy/core/models/datasource.py,sha256=c0tGxyH2WwTmAD047tr69U0a6GNVf-ug26H68yii7DA,9257
|
|
28
|
-
trilogy/core/models/environment.py,sha256=
|
|
28
|
+
trilogy/core/models/environment.py,sha256=h06y1Dv7naw2GuFFAAyoFZmicG7a7Lu-dRoYPVfrOGo,25967
|
|
29
29
|
trilogy/core/models/execute.py,sha256=ABylFQgtavjjCfFkEsFdUwfMB4UBQLHjdzQ9E67QlAE,33521
|
|
30
30
|
trilogy/core/optimizations/__init__.py,sha256=EBanqTXEzf1ZEYjAneIWoIcxtMDite5-n2dQ5xcfUtg,356
|
|
31
31
|
trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnbnb6vdVIztUfFQZsGnM,513
|
|
@@ -33,7 +33,7 @@ trilogy/core/optimizations/inline_constant.py,sha256=lvNTIXaLNkw3HseJyXyDNk5R52d
|
|
|
33
33
|
trilogy/core/optimizations/inline_datasource.py,sha256=AHuTGh2x0GQ8usOe0NiFncfTFQ_KogdgDl4uucmhIbI,4241
|
|
34
34
|
trilogy/core/optimizations/predicate_pushdown.py,sha256=g4AYE8Aw_iMlAh68TjNXGP754NTurrDduFECkUjoBnc,9399
|
|
35
35
|
trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
-
trilogy/core/processing/concept_strategies_v3.py,sha256=
|
|
36
|
+
trilogy/core/processing/concept_strategies_v3.py,sha256=wPlpg4L7uw-f0DgJBkI8VRdcisjDT1X6iApjEE6CmfA,40291
|
|
37
37
|
trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
|
|
38
38
|
trilogy/core/processing/utility.py,sha256=Oc5tLGeDDpzhbfo2ZcF8ex1kez-NcJDMcG2Lm5BjS4c,20548
|
|
39
39
|
trilogy/core/processing/node_generators/__init__.py,sha256=o8rOFHPSo-s_59hREwXMW6gjUJCsiXumdbJNozHUf-Y,800
|
|
@@ -88,14 +88,14 @@ trilogy/parsing/common.py,sha256=yAE3x4SyO4PfAb7HhZ_l9sNPYaf_pcM1K8ioEy76SCU,203
|
|
|
88
88
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
89
89
|
trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
|
|
90
90
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
91
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
91
|
+
trilogy/parsing/parse_engine.py,sha256=32_yO_SreTjHxCkMziW2re15ilEZn01OUizVAvN9xHo,54656
|
|
92
92
|
trilogy/parsing/render.py,sha256=o_XuQWhcwx1lD9eGVqkqZEwkmQK0HdmWWokGBtdeH4I,17837
|
|
93
93
|
trilogy/parsing/trilogy.lark,sha256=EazfEvYPuvkPkNjUnVzFi0uD9baavugbSI8CyfawShk,12573
|
|
94
94
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
95
95
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
96
|
-
pytrilogy-0.0.3.
|
|
97
|
-
pytrilogy-0.0.3.
|
|
98
|
-
pytrilogy-0.0.3.
|
|
99
|
-
pytrilogy-0.0.3.
|
|
100
|
-
pytrilogy-0.0.3.
|
|
101
|
-
pytrilogy-0.0.3.
|
|
96
|
+
pytrilogy-0.0.3.6.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
97
|
+
pytrilogy-0.0.3.6.dist-info/METADATA,sha256=PBxZLl7AH82ztlgGbJbdLWwj8r3Wo2_JRsXxgh5y1Gc,8983
|
|
98
|
+
pytrilogy-0.0.3.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
99
|
+
pytrilogy-0.0.3.6.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
|
|
100
|
+
pytrilogy-0.0.3.6.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
101
|
+
pytrilogy-0.0.3.6.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/models/author.py
CHANGED
|
@@ -166,10 +166,8 @@ class UndefinedConcept(ConceptRef):
|
|
|
166
166
|
|
|
167
167
|
|
|
168
168
|
def address_with_namespace(address: str, namespace: str) -> str:
|
|
169
|
-
|
|
170
|
-
if
|
|
171
|
-
return address
|
|
172
|
-
if ns == DEFAULT_NAMESPACE:
|
|
169
|
+
existing_ns = address.split(".", 1)[0]
|
|
170
|
+
if existing_ns == DEFAULT_NAMESPACE:
|
|
173
171
|
return f"{namespace}.{address.split('.',1)[1]}"
|
|
174
172
|
return f"{namespace}.{address}"
|
|
175
173
|
|
|
@@ -203,7 +201,7 @@ class Parenthetical(
|
|
|
203
201
|
def __repr__(self):
|
|
204
202
|
return f"({str(self.content)})"
|
|
205
203
|
|
|
206
|
-
def with_namespace(self, namespace: str):
|
|
204
|
+
def with_namespace(self, namespace: str) -> Parenthetical:
|
|
207
205
|
return Parenthetical.model_construct(
|
|
208
206
|
content=(
|
|
209
207
|
self.content.with_namespace(namespace)
|
|
@@ -917,8 +915,6 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
917
915
|
return self.name.replace(".", "_")
|
|
918
916
|
|
|
919
917
|
def with_namespace(self, namespace: str) -> Self:
|
|
920
|
-
if namespace == self.namespace:
|
|
921
|
-
return self
|
|
922
918
|
return self.__class__.model_construct(
|
|
923
919
|
name=self.name,
|
|
924
920
|
datatype=self.datatype,
|
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from typing import (
|
|
9
9
|
TYPE_CHECKING,
|
|
10
10
|
Annotated,
|
|
11
|
+
Any,
|
|
11
12
|
Dict,
|
|
12
13
|
ItemsView,
|
|
13
14
|
List,
|
|
@@ -383,14 +384,13 @@ class Environment(BaseModel):
|
|
|
383
384
|
):
|
|
384
385
|
exists = True
|
|
385
386
|
imp_stm = Import(alias=alias, path=Path(source.working_path))
|
|
386
|
-
same_namespace = alias ==
|
|
387
|
+
same_namespace = alias == DEFAULT_NAMESPACE
|
|
387
388
|
|
|
388
389
|
if not exists:
|
|
389
390
|
self.imports[alias].append(imp_stm)
|
|
390
391
|
# we can't exit early
|
|
391
392
|
# as there may be new concepts
|
|
392
393
|
for k, concept in source.concepts.items():
|
|
393
|
-
|
|
394
394
|
# skip internal namespace
|
|
395
395
|
if INTERNAL_NAMESPACE in concept.address:
|
|
396
396
|
continue
|
|
@@ -416,7 +416,6 @@ class Environment(BaseModel):
|
|
|
416
416
|
self.alias_origin_lookup[address_with_namespace(key, alias)] = (
|
|
417
417
|
val.with_namespace(alias)
|
|
418
418
|
)
|
|
419
|
-
|
|
420
419
|
return self
|
|
421
420
|
|
|
422
421
|
def add_file_import(
|
|
@@ -427,7 +426,6 @@ class Environment(BaseModel):
|
|
|
427
426
|
from trilogy.parsing.parse_engine import (
|
|
428
427
|
PARSER,
|
|
429
428
|
ParseToObjects,
|
|
430
|
-
gen_cache_lookup,
|
|
431
429
|
)
|
|
432
430
|
|
|
433
431
|
if isinstance(path, str):
|
|
@@ -441,7 +439,8 @@ class Environment(BaseModel):
|
|
|
441
439
|
else:
|
|
442
440
|
target = path
|
|
443
441
|
if not env:
|
|
444
|
-
|
|
442
|
+
import_keys = ["root", alias]
|
|
443
|
+
parse_address = "-".join(import_keys)
|
|
445
444
|
try:
|
|
446
445
|
with open(target, "r", encoding="utf-8") as f:
|
|
447
446
|
text = f.read()
|
|
@@ -455,10 +454,13 @@ class Environment(BaseModel):
|
|
|
455
454
|
),
|
|
456
455
|
parse_address=parse_address,
|
|
457
456
|
token_address=target,
|
|
457
|
+
import_keys=import_keys,
|
|
458
458
|
)
|
|
459
459
|
nparser.set_text(text)
|
|
460
|
+
nparser.environment.concepts.fail_on_missing = False
|
|
460
461
|
nparser.transform(PARSER.parse(text))
|
|
461
|
-
nparser.
|
|
462
|
+
nparser.run_second_parse_pass()
|
|
463
|
+
nparser.environment.concepts.fail_on_missing = True
|
|
462
464
|
|
|
463
465
|
except Exception as e:
|
|
464
466
|
raise ImportError(
|
|
@@ -664,37 +666,59 @@ class LazyEnvironment(Environment):
|
|
|
664
666
|
until relevant attributes accessed."""
|
|
665
667
|
|
|
666
668
|
load_path: Path
|
|
669
|
+
setup_queries: list[Any] = Field(default_factory=list)
|
|
667
670
|
loaded: bool = False
|
|
668
671
|
|
|
672
|
+
@property
|
|
673
|
+
def setup_path(self) -> Path:
|
|
674
|
+
return self.load_path.parent / "setup.preql"
|
|
675
|
+
|
|
669
676
|
def __init__(self, **data):
|
|
677
|
+
if not data.get("working_path"):
|
|
678
|
+
data["working_path"] = data["load_path"].parent
|
|
670
679
|
super().__init__(**data)
|
|
680
|
+
assert self.working_path == self.load_path.parent
|
|
671
681
|
|
|
672
682
|
def _add_path_concepts(self):
|
|
673
683
|
pass
|
|
674
684
|
|
|
685
|
+
def _load(self):
|
|
686
|
+
if self.loaded:
|
|
687
|
+
return
|
|
688
|
+
from trilogy import parse
|
|
689
|
+
|
|
690
|
+
env = Environment(working_path=self.load_path.parent)
|
|
691
|
+
assert env.working_path == self.load_path.parent
|
|
692
|
+
with open(self.load_path, "r") as f:
|
|
693
|
+
env, _ = parse(f.read(), env)
|
|
694
|
+
if self.setup_path.exists():
|
|
695
|
+
with open(self.setup_path, "r") as f2:
|
|
696
|
+
env, q = parse(f2.read(), env)
|
|
697
|
+
for q in q:
|
|
698
|
+
self.setup_queries.append(q)
|
|
699
|
+
self.loaded = True
|
|
700
|
+
self.datasources = env.datasources
|
|
701
|
+
self.concepts = env.concepts
|
|
702
|
+
self.imports = env.imports
|
|
703
|
+
self.alias_origin_lookup = env.alias_origin_lookup
|
|
704
|
+
self.materialized_concepts = env.materialized_concepts
|
|
705
|
+
self.functions = env.functions
|
|
706
|
+
self.data_types = env.data_types
|
|
707
|
+
self.cte_name_map = env.cte_name_map
|
|
708
|
+
|
|
675
709
|
def __getattribute__(self, name):
|
|
676
|
-
if name in (
|
|
677
|
-
"
|
|
678
|
-
"
|
|
679
|
-
"
|
|
680
|
-
"
|
|
681
|
-
"
|
|
682
|
-
"
|
|
710
|
+
if name not in (
|
|
711
|
+
"datasources",
|
|
712
|
+
"concepts",
|
|
713
|
+
"imports",
|
|
714
|
+
"materialized_concepts",
|
|
715
|
+
"functions",
|
|
716
|
+
"datatypes",
|
|
717
|
+
"cte_name_map",
|
|
683
718
|
) or name.startswith("_"):
|
|
684
719
|
return super().__getattribute__(name)
|
|
685
720
|
if not self.loaded:
|
|
686
|
-
|
|
687
|
-
f"lazily evaluating load path {self.load_path} to access {name}"
|
|
688
|
-
)
|
|
689
|
-
from trilogy import parse
|
|
690
|
-
|
|
691
|
-
env = Environment(working_path=str(self.working_path))
|
|
692
|
-
with open(self.load_path, "r") as f:
|
|
693
|
-
parse(f.read(), env)
|
|
694
|
-
self.loaded = True
|
|
695
|
-
self.datasources = env.datasources
|
|
696
|
-
self.concepts = env.concepts
|
|
697
|
-
self.imports = env.imports
|
|
721
|
+
self._load()
|
|
698
722
|
return super().__getattribute__(name)
|
|
699
723
|
|
|
700
724
|
|
|
@@ -1046,7 +1046,7 @@ def source_query_concepts(
|
|
|
1046
1046
|
f"{c.address}<{c.purpose}>{c.derivation}>" for c in output_concepts
|
|
1047
1047
|
]
|
|
1048
1048
|
raise ValueError(
|
|
1049
|
-
f"Could not resolve
|
|
1049
|
+
f"Could not resolve connections between {error_strings} from environment graph."
|
|
1050
1050
|
)
|
|
1051
1051
|
final = [x for x in root.output_concepts if x.address not in root.hidden_concepts]
|
|
1052
1052
|
logger.info(
|
trilogy/core/query_processor.py
CHANGED
|
@@ -385,7 +385,7 @@ def get_query_node(
|
|
|
385
385
|
)
|
|
386
386
|
graph = generate_graph(build_environment)
|
|
387
387
|
logger.info(
|
|
388
|
-
f"{LOGGER_PREFIX} getting source datasource for outputs {
|
|
388
|
+
f"{LOGGER_PREFIX} getting source datasource for outputs {build_statement.output_components} grain {build_statement.grain}"
|
|
389
389
|
)
|
|
390
390
|
|
|
391
391
|
search_concepts: list[BuildConcept] = build_statement.output_components
|
trilogy/executor.py
CHANGED
|
@@ -346,10 +346,10 @@ class Executor(object):
|
|
|
346
346
|
file = Path(file)
|
|
347
347
|
with open(file, "r") as f:
|
|
348
348
|
command = f.read()
|
|
349
|
-
return self.parse_text_generator(command, persist=persist)
|
|
349
|
+
return self.parse_text_generator(command, persist=persist, root=file)
|
|
350
350
|
|
|
351
351
|
def parse_text(
|
|
352
|
-
self, command: str, persist: bool = False
|
|
352
|
+
self, command: str, persist: bool = False, root: Path | None = None
|
|
353
353
|
) -> List[
|
|
354
354
|
ProcessedQuery
|
|
355
355
|
| ProcessedQueryPersist
|
|
@@ -357,9 +357,11 @@ class Executor(object):
|
|
|
357
357
|
| ProcessedRawSQLStatement
|
|
358
358
|
| ProcessedCopyStatement
|
|
359
359
|
]:
|
|
360
|
-
return list(self.parse_text_generator(command, persist=persist))
|
|
360
|
+
return list(self.parse_text_generator(command, persist=persist, root=root))
|
|
361
361
|
|
|
362
|
-
def parse_text_generator(
|
|
362
|
+
def parse_text_generator(
|
|
363
|
+
self, command: str, persist: bool = False, root: Path | None = None
|
|
364
|
+
) -> Generator[
|
|
363
365
|
ProcessedQuery
|
|
364
366
|
| ProcessedQueryPersist
|
|
365
367
|
| ProcessedShowStatement
|
|
@@ -369,7 +371,7 @@ class Executor(object):
|
|
|
369
371
|
None,
|
|
370
372
|
]:
|
|
371
373
|
"""Process a preql text command"""
|
|
372
|
-
_, parsed = parse_text(command, self.environment)
|
|
374
|
+
_, parsed = parse_text(command, self.environment, root=root)
|
|
373
375
|
generatable = [
|
|
374
376
|
x
|
|
375
377
|
for x in parsed
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -129,6 +129,8 @@ CONSTANT_TYPES = (int, float, str, bool, list, ListWrapper, MapWrapper)
|
|
|
129
129
|
|
|
130
130
|
SELF_LABEL = "root"
|
|
131
131
|
|
|
132
|
+
MAX_PARSE_DEPTH = 10
|
|
133
|
+
|
|
132
134
|
|
|
133
135
|
@dataclass
|
|
134
136
|
class WholeGrainWrapper:
|
|
@@ -146,10 +148,6 @@ with open(join(dirname(__file__), "trilogy.lark"), "r") as f:
|
|
|
146
148
|
)
|
|
147
149
|
|
|
148
150
|
|
|
149
|
-
def gen_cache_lookup(path: str, alias: str, parent: str) -> str:
|
|
150
|
-
return path + alias + parent
|
|
151
|
-
|
|
152
|
-
|
|
153
151
|
def parse_concept_reference(
|
|
154
152
|
name: str, environment: Environment, purpose: Optional[Purpose] = None
|
|
155
153
|
) -> Tuple[str, str, str, str | None]:
|
|
@@ -223,6 +221,8 @@ class ParseToObjects(Transformer):
|
|
|
223
221
|
parsed: dict[str, "ParseToObjects"] | None = None,
|
|
224
222
|
tokens: dict[Path | str, ParseTree] | None = None,
|
|
225
223
|
text_lookup: dict[Path | str, str] | None = None,
|
|
224
|
+
environment_lookup: dict[str, Environment] | None = None,
|
|
225
|
+
import_keys: list[str] | None = None,
|
|
226
226
|
):
|
|
227
227
|
Transformer.__init__(self, True)
|
|
228
228
|
self.environment: Environment = environment
|
|
@@ -230,6 +230,7 @@ class ParseToObjects(Transformer):
|
|
|
230
230
|
self.token_address: Path | str = token_address or SELF_LABEL
|
|
231
231
|
self.parsed: dict[str, ParseToObjects] = parsed if parsed is not None else {}
|
|
232
232
|
self.tokens: dict[Path | str, ParseTree] = tokens if tokens is not None else {}
|
|
233
|
+
self.environments: dict[str, Environment] = environment_lookup or {}
|
|
233
234
|
self.text_lookup: dict[Path | str, str] = (
|
|
234
235
|
text_lookup if text_lookup is not None else {}
|
|
235
236
|
)
|
|
@@ -237,6 +238,7 @@ class ParseToObjects(Transformer):
|
|
|
237
238
|
# after initial parsing
|
|
238
239
|
self.parse_pass = ParsePass.INITIAL
|
|
239
240
|
self.function_factory = FunctionFactory(self.environment)
|
|
241
|
+
self.import_keys: list[str] = import_keys or ["root"]
|
|
240
242
|
|
|
241
243
|
def set_text(self, text: str):
|
|
242
244
|
self.text_lookup[self.token_address] = text
|
|
@@ -252,12 +254,14 @@ class ParseToObjects(Transformer):
|
|
|
252
254
|
for _, v in self.parsed.items():
|
|
253
255
|
v.prepare_parse()
|
|
254
256
|
|
|
255
|
-
def
|
|
257
|
+
def run_second_parse_pass(self, force: bool = False):
|
|
258
|
+
if self.token_address not in self.tokens:
|
|
259
|
+
return []
|
|
256
260
|
self.parse_pass = ParsePass.VALIDATION
|
|
257
|
-
for
|
|
261
|
+
for _, v in list(self.parsed.items()):
|
|
258
262
|
if v.parse_pass == ParsePass.VALIDATION:
|
|
259
263
|
continue
|
|
260
|
-
v.
|
|
264
|
+
v.run_second_parse_pass()
|
|
261
265
|
reparsed = self.transform(self.tokens[self.token_address])
|
|
262
266
|
self.environment.concepts.undefined = {}
|
|
263
267
|
return reparsed
|
|
@@ -301,11 +305,6 @@ class ParseToObjects(Transformer):
|
|
|
301
305
|
def QUOTED_IDENTIFIER(self, args) -> str:
|
|
302
306
|
return args.value[1:-1]
|
|
303
307
|
|
|
304
|
-
# @v_args(meta=True)
|
|
305
|
-
# def concept_lit(self, meta: Meta, args) -> ConceptRef:
|
|
306
|
-
# address = args[0]
|
|
307
|
-
# return self.environment.concepts.__getitem__(address, meta.line)
|
|
308
|
-
# return ConceptRef(address=address, line_no=meta.line)
|
|
309
308
|
@v_args(meta=True)
|
|
310
309
|
def concept_lit(self, meta: Meta, args) -> ConceptRef:
|
|
311
310
|
address = args[0]
|
|
@@ -390,7 +389,6 @@ class ParseToObjects(Transformer):
|
|
|
390
389
|
|
|
391
390
|
@v_args(meta=True)
|
|
392
391
|
def column_assignment(self, meta: Meta, args):
|
|
393
|
-
# TODO -> deal with conceptual modifiers
|
|
394
392
|
modifiers = []
|
|
395
393
|
alias = args[0]
|
|
396
394
|
concept_list = args[1]
|
|
@@ -853,8 +851,10 @@ class ParseToObjects(Transformer):
|
|
|
853
851
|
def import_statement(self, args: list[str]) -> ImportStatement:
|
|
854
852
|
if len(args) == 2:
|
|
855
853
|
alias = args[-1]
|
|
854
|
+
cache_key = args[-1]
|
|
856
855
|
else:
|
|
857
856
|
alias = self.environment.namespace
|
|
857
|
+
cache_key = args[0]
|
|
858
858
|
path = args[0].split(".")
|
|
859
859
|
|
|
860
860
|
target = join(self.environment.working_path, *path) + ".preql"
|
|
@@ -862,10 +862,14 @@ class ParseToObjects(Transformer):
|
|
|
862
862
|
# tokens + text are cached by path
|
|
863
863
|
token_lookup = Path(target)
|
|
864
864
|
|
|
865
|
-
#
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
865
|
+
# parser + env has to be cached by prior import path + current key
|
|
866
|
+
key_path = self.import_keys + [cache_key]
|
|
867
|
+
cache_lookup = "-".join(key_path)
|
|
868
|
+
|
|
869
|
+
# we don't iterate past the max parse depth
|
|
870
|
+
if len(key_path) > MAX_PARSE_DEPTH:
|
|
871
|
+
return ImportStatement(alias=alias, path=Path(target))
|
|
872
|
+
|
|
869
873
|
if token_lookup in self.tokens:
|
|
870
874
|
raw_tokens = self.tokens[token_lookup]
|
|
871
875
|
text = self.text_lookup[token_lookup]
|
|
@@ -879,10 +883,14 @@ class ParseToObjects(Transformer):
|
|
|
879
883
|
if cache_lookup in self.parsed:
|
|
880
884
|
nparser = self.parsed[cache_lookup]
|
|
881
885
|
new_env = nparser.environment
|
|
886
|
+
if nparser.parse_pass != ParsePass.VALIDATION:
|
|
887
|
+
# nparser.transform(raw_tokens)
|
|
888
|
+
nparser.run_second_parse_pass()
|
|
882
889
|
else:
|
|
883
890
|
try:
|
|
884
891
|
new_env = Environment(
|
|
885
892
|
working_path=dirname(target),
|
|
893
|
+
env_file_path=token_lookup,
|
|
886
894
|
)
|
|
887
895
|
new_env.concepts.fail_on_missing = False
|
|
888
896
|
self.parsed[self.parse_address] = self
|
|
@@ -893,6 +901,7 @@ class ParseToObjects(Transformer):
|
|
|
893
901
|
parsed=self.parsed,
|
|
894
902
|
tokens=self.tokens,
|
|
895
903
|
text_lookup=self.text_lookup,
|
|
904
|
+
import_keys=self.import_keys + [cache_key],
|
|
896
905
|
)
|
|
897
906
|
nparser.transform(raw_tokens)
|
|
898
907
|
self.parsed[cache_lookup] = nparser
|
|
@@ -901,9 +910,11 @@ class ParseToObjects(Transformer):
|
|
|
901
910
|
f"Unable to import file {target}, parsing error: {e}"
|
|
902
911
|
) from e
|
|
903
912
|
|
|
904
|
-
|
|
913
|
+
parsed_path = Path(args[0])
|
|
914
|
+
imps = ImportStatement(alias=alias, path=parsed_path)
|
|
915
|
+
|
|
905
916
|
self.environment.add_import(
|
|
906
|
-
alias, new_env, Import(alias=alias, path=
|
|
917
|
+
alias, new_env, Import(alias=alias, path=parsed_path)
|
|
907
918
|
)
|
|
908
919
|
return imps
|
|
909
920
|
|
|
@@ -1594,7 +1605,9 @@ def parse_text_raw(text: str, environment: Optional[Environment] = None):
|
|
|
1594
1605
|
PARSER.parse(text)
|
|
1595
1606
|
|
|
1596
1607
|
|
|
1597
|
-
def parse_text(
|
|
1608
|
+
def parse_text(
|
|
1609
|
+
text: str, environment: Optional[Environment] = None, root: Path | None = None
|
|
1610
|
+
) -> Tuple[
|
|
1598
1611
|
Environment,
|
|
1599
1612
|
List[
|
|
1600
1613
|
Datasource
|
|
@@ -1606,8 +1619,10 @@ def parse_text(text: str, environment: Optional[Environment] = None) -> Tuple[
|
|
|
1606
1619
|
| None
|
|
1607
1620
|
],
|
|
1608
1621
|
]:
|
|
1609
|
-
environment = environment or
|
|
1610
|
-
|
|
1622
|
+
environment = environment or (
|
|
1623
|
+
Environment(working_path=root) if root else Environment()
|
|
1624
|
+
)
|
|
1625
|
+
parser = ParseToObjects(environment=environment, import_keys=["root"])
|
|
1611
1626
|
|
|
1612
1627
|
try:
|
|
1613
1628
|
parser.set_text(text)
|
|
@@ -1615,7 +1630,7 @@ def parse_text(text: str, environment: Optional[Environment] = None) -> Tuple[
|
|
|
1615
1630
|
parser.prepare_parse()
|
|
1616
1631
|
parser.transform(PARSER.parse(text))
|
|
1617
1632
|
# this will reset fail on missing
|
|
1618
|
-
pass_two = parser.
|
|
1633
|
+
pass_two = parser.run_second_parse_pass()
|
|
1619
1634
|
output = [v for v in pass_two if v]
|
|
1620
1635
|
environment.concepts.fail_on_missing = True
|
|
1621
1636
|
except VisitError as e:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|