Skip to content

Commit e119d0e

Browse files
Undo some changes
1 parent a366ac9 commit e119d0e

1 file changed

Lines changed: 53 additions & 67 deletions

File tree

multilingualprogramming/__main__.py

Lines changed: 53 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
from multilingualprogramming.exceptions import UnsupportedLanguageError
4444
from multilingualprogramming.lexer.lexer import Lexer
4545
from multilingualprogramming.parser.parser import Parser
46-
from multilingualprogramming.parser.ast_nodes import ImportStatement, FromImportStatement, Program
4746
from multilingualprogramming.runtime.ai_runtime import AIRuntime, MockProvider
4847
from multilingualprogramming.source_extensions import (
4948
find_module_source,
@@ -107,75 +106,60 @@ def _parse_program_from_file(path: str, lang: str | None):
107106
return parser.parse()
108107

109108

110-
def _resolve_module_path(module_name: str, base_dir: Path) -> Path | None:
111-
"""Resolve a module name (e.g., 'ui.composants.barre_recherche') to a file path."""
112-
# Convert dot notation to file path
113-
parts = module_name.split(".")
114-
# Try relative to base directory first
115-
candidate = base_dir.joinpath(*parts).with_suffix(".multi")
116-
if candidate.exists():
117-
return candidate
118-
# Try one level up (in case we're in a subdirectory)
119-
candidate = base_dir.parent.joinpath(*parts).with_suffix(".multi")
120-
if candidate.exists():
121-
return candidate
122-
return None
123-
124-
125-
def _merge_programs(main_program: Program, imported_program: Program) -> Program:
126-
"""Merge imported program statements into main program."""
127-
# Combine statements, removing duplicate imports
128-
seen_imports = set()
129-
merged_statements = []
130-
131-
# Add unique imports from both programs
132-
for stmt in main_program.body + imported_program.body:
133-
if isinstance(stmt, (ImportStatement, FromImportStatement)):
134-
# Create a unique key for the import
135-
key = (type(stmt).__name__, getattr(stmt, 'module', ''))
136-
if key not in seen_imports:
137-
seen_imports.add(key)
138-
merged_statements.append(stmt)
139-
else:
140-
merged_statements.append(stmt)
141-
142-
# Create new program with merged statements
143-
new_program = Program(merged_statements)
144-
return new_program
145-
109+
def _parse_ir_from_file(path: str | Path, lang: str | None):
110+
resolved = Path(path)
111+
source = _read_source_file(str(resolved))
112+
lexer = Lexer(source, language=lang)
113+
tokens = lexer.tokenize()
114+
detected_lang = lexer.language or lang or "en"
115+
parser = Parser(tokens, source_language=detected_lang)
116+
program = parser.parse()
117+
return lower_to_semantic_ir(program, detected_lang)
146118

147-
def _parse_program_with_dependencies(path: str, lang: str | None) -> Program:
148-
"""Parse a program and recursively include all imported modules."""
149-
main_program = _parse_program_from_file(path, lang)
150-
base_dir = Path(path).resolve().parent
151-
processed = set()
152119

153-
def _collect_imports(program: Program, current_dir: Path) -> Program:
154-
"""Recursively collect imports and merge programs."""
155-
result = program
120+
def _resolve_absolute_module_source(entry_file: Path, module_name: str) -> Path | None:
121+
parts = module_name.split(".")
122+
search_root = entry_file.resolve().parent
123+
while True:
124+
base = search_root.joinpath(*parts[:-1]) if len(parts) > 1 else search_root
125+
candidate = find_module_source(base, parts[-1])
126+
if candidate is not None:
127+
return candidate
128+
package_dir = search_root.joinpath(*parts)
129+
package_init = find_package_init(package_dir)
130+
if package_init is not None:
131+
return package_init
132+
if search_root.parent == search_root:
133+
return None
134+
search_root = search_root.parent
156135

157-
for stmt in program.body:
158-
if isinstance(stmt, ImportStatement) and stmt.module:
159-
# Skip if already processed
160-
if stmt.module in processed:
161-
continue
162-
processed.add(stmt.module)
163136

164-
# Resolve module path
165-
module_path = _resolve_module_path(stmt.module, current_dir)
166-
if module_path:
167-
try:
168-
imported_program = _parse_program_from_file(str(module_path), lang)
169-
# Recursively collect dependencies of imported module
170-
imported_program = _collect_imports(imported_program, module_path.parent)
171-
# Merge into result
172-
result = _merge_programs(result, imported_program)
173-
except Exception as e:
174-
print(f"[WARN] Failed to import {stmt.module}: {e}")
137+
def _collect_ui_import_modules(entry_file: Path, root_ir, lang: str | None):
138+
modules = {}
139+
warnings = []
140+
visited = {entry_file.resolve()}
175141

176-
return result
142+
def visit(ir_program, current_file: Path):
143+
for node in ir_program.body:
144+
if not isinstance(node, IRImportStatement):
145+
continue
146+
module_path = _resolve_absolute_module_source(current_file, node.module)
147+
if module_path is None:
148+
continue
149+
resolved = module_path.resolve()
150+
if resolved in visited:
151+
continue
152+
visited.add(resolved)
153+
try:
154+
imported_ir = _parse_ir_from_file(resolved, lang)
155+
except Exception as exc: # pylint: disable=broad-exception-caught
156+
warnings.append(f"Skipped UI import {node.module}: {exc}")
157+
continue
158+
visit(imported_ir, resolved)
159+
modules[node.module] = imported_ir
177160

178-
return _collect_imports(main_program, base_dir)
161+
visit(root_ir, entry_file.resolve())
162+
return modules, warnings
179163

180164

181165
def cmd_run(args):
@@ -358,9 +342,11 @@ def cmd_build_wasm_bundle(args):
358342

359343
def cmd_build_ui_bundle(args):
360344
"""Build a self-contained reactive UI bundle (HTML + JS)."""
361-
program = _parse_program_with_dependencies(args.file, args.lang)
362-
ir = lower_to_semantic_ir(program, args.lang or "en")
363-
result = lower_to_ui(ir)
345+
entry_file = Path(args.file)
346+
ir = _parse_ir_from_file(entry_file, args.lang)
347+
modules, import_warnings = _collect_ui_import_modules(entry_file, ir, args.lang)
348+
result = lower_to_ui(ir, modules=modules)
349+
result.diagnostics.extend(import_warnings)
364350

365351
# Create output directory
366352
out_dir = Path(args.out_dir)

0 commit comments

Comments
 (0)