• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# coding=utf-8
2#
3# Copyright (c) 2025 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""Orchestrates the compilation process.
17
18- BackendRegistry: initializes all known backends
19- CompilerInvocation: constructs the invocation from cmdline
20    - Parses the general command line arguments
21    - Enables user specified backends
22    - Parses backend-specific arguments
23    - Sets backend options
24- CompilerInstance: runs the compilation
25    - CompilerInstance: scans and parses sources files
26    - Backends: post-process the IR
27    - Backends: validate the IR
28    - Backends: generate the output
29"""
30
31from dataclasses import dataclass, field
32from itertools import chain
33from pathlib import Path
34
35from taihe.driver.backend import Backend, BackendConfig
36from taihe.semantics.analysis import analyze_semantics
37from taihe.semantics.declarations import PackageGroup
38from taihe.utils.analyses import AnalysisManager
39from taihe.utils.diagnostics import ConsoleDiagnosticsManager, DiagnosticsManager
40from taihe.utils.exceptions import IgnoredFileReason, IgnoredFileWarn
41from taihe.utils.outputs import OutputConfig
42from taihe.utils.sources import SourceFile, SourceLocation, SourceManager
43
44
45def validate_source_file(source: SourceFile) -> IgnoredFileWarn | None:
46    # subdirectories are ignored
47    if not source.path.is_file():
48        return IgnoredFileWarn(
49            IgnoredFileReason.IS_DIRECTORY,
50            loc=SourceLocation(source),
51        )
52    # unexpected file extension
53    if source.path.suffix != ".taihe":
54        return IgnoredFileWarn(
55            IgnoredFileReason.EXTENSION_MISMATCH,
56            loc=SourceLocation(source),
57        )
58    return None
59
60
61@dataclass
62class CompilerInvocation:
63    """Describes the options and intents for a compiler invocation.
64
65    CompilerInvocation stores the high-level intent in a structured way, such
66    as the input paths, the target for code generation. Generally speaking, it
67    can be considered as the parsed and verified version of a compiler's
68    command line flags.
69
70    CompilerInvocation does not manage the internal state. Use
71    `CompilerInstance` instead.
72    """
73
74    src_files: list[Path] = field(default_factory=lambda: [])
75    src_dirs: list[Path] = field(default_factory=lambda: [])
76    output_config: OutputConfig = field(default_factory=OutputConfig)
77    backends: list[BackendConfig] = field(default_factory=lambda: [])
78
79    # TODO: refactor this to a more structured way
80    sts_keep_name: bool = False
81    arkts_module_prefix: str | None = None
82    arkts_path_prefix: str | None = None
83
84
85class CompilerInstance:
86    """Helper class for storing key objects.
87
88    CompilerInstance holds key intermediate objects across the compilation
89    process, such as the source manager and the diagnostics manager.
90
91    It also provides utility methods for driving the compilation process.
92    """
93
94    invocation: CompilerInvocation
95    backends: list[Backend]
96
97    diagnostics_manager: DiagnosticsManager
98
99    source_manager: SourceManager
100    package_group: PackageGroup
101
102    analysis_manager: AnalysisManager
103
104    def __init__(
105        self,
106        invocation: CompilerInvocation,
107        *,
108        dm: type[DiagnosticsManager] = ConsoleDiagnosticsManager,
109    ):
110        self.invocation = invocation
111        self.diagnostics_manager = dm()
112        self.analysis_manager = AnalysisManager(invocation, self.diagnostics_manager)
113        self.source_manager = SourceManager()
114        self.package_group = PackageGroup()
115        self.output_manager = invocation.output_config.construct(self)
116        self.backends = [conf.construct(self) for conf in invocation.backends]
117
118    ##########################
119    # The compilation phases #
120    ##########################
121
122    def collect(self):
123        """Adds all `.taihe` files inside a directory. Subdirectories are ignored."""
124        scanned = chain.from_iterable(p.iterdir() for p in self.invocation.src_dirs)
125        direct = self.invocation.src_files
126
127        for file in chain(direct, scanned):
128            source = SourceFile(file)
129            if warning := validate_source_file(source):
130                self.diagnostics_manager.emit(warning)
131            else:
132                self.source_manager.add_source(source)
133
134    def parse(self):
135        from taihe.parse.convert import AstConverter
136
137        for src in self.source_manager.sources:
138            with self.diagnostics_manager.capture_error():
139                conv = AstConverter(src, self.diagnostics_manager)
140                pkg = conv.convert()
141                self.package_group.add(pkg)
142
143        for b in self.backends:
144            b.post_process()
145
146    def validate(self):
147        analyze_semantics(self.package_group, self.diagnostics_manager)
148
149        for b in self.backends:
150            b.validate()
151
152    def generate(self):
153        if self.diagnostics_manager.has_error:
154            return
155
156        for b in self.backends:
157            b.generate()
158
159        self.output_manager.post_generate()
160
161    def run(self):
162        self.collect()
163        self.parse()
164        self.validate()
165        self.generate()
166        return not self.diagnostics_manager.has_error